// Copyright 2015 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "device/usb/usb_device_android.h" #include "base/android/build_info.h" #include "base/android/jni_string.h" #include "base/bind.h" #include "base/location.h" #include "base/threading/thread_task_runner_handle.h" #include "device/usb/usb_configuration_android.h" #include "device/usb/usb_descriptors.h" #include "device/usb/usb_device_handle_android.h" #include "device/usb/usb_interface_android.h" #include "device/usb/usb_service_android.h" #include "device/usb/webusb_descriptors.h" #include "jni/ChromeUsbDevice_jni.h" using base::android::ConvertJavaStringToUTF16; using base::android::JavaRef; using base::android::ScopedJavaLocalRef; namespace device { // static scoped_refptr UsbDeviceAndroid::Create( JNIEnv* env, base::WeakPtr service, const JavaRef& usb_device) { ScopedJavaLocalRef wrapper = Java_ChromeUsbDevice_create(env, usb_device); uint16_t device_version = 0; if (base::android::BuildInfo::GetInstance()->sdk_int() >= 23) device_version = Java_ChromeUsbDevice_getDeviceVersion(env, wrapper); base::string16 manufacturer_string, product_string, serial_number; if (base::android::BuildInfo::GetInstance()->sdk_int() >= 21) { manufacturer_string = ConvertJavaStringToUTF16( env, Java_ChromeUsbDevice_getManufacturerName(env, wrapper)); product_string = ConvertJavaStringToUTF16( env, Java_ChromeUsbDevice_getProductName(env, wrapper)); serial_number = ConvertJavaStringToUTF16( env, Java_ChromeUsbDevice_getSerialNumber(env, wrapper)); } return make_scoped_refptr(new UsbDeviceAndroid( env, service, 0x0200, // USB protocol version, not provided by the Android API. Java_ChromeUsbDevice_getDeviceClass(env, wrapper), Java_ChromeUsbDevice_getDeviceSubclass(env, wrapper), Java_ChromeUsbDevice_getDeviceProtocol(env, wrapper), Java_ChromeUsbDevice_getVendorId(env, wrapper), Java_ChromeUsbDevice_getProductId(env, wrapper), device_version, manufacturer_string, product_string, serial_number, wrapper)); } void UsbDeviceAndroid::RequestPermission(const ResultCallback& callback) { if (!permission_granted_ && service_) { request_permission_callbacks_.push_back(callback); service_->RequestDevicePermission(j_object_, device_id_); } else { base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::Bind(callback, permission_granted_)); } } void UsbDeviceAndroid::Open(const OpenCallback& callback) { scoped_refptr device_handle; if (service_) { JNIEnv* env = base::android::AttachCurrentThread(); ScopedJavaLocalRef connection = service_->OpenDevice(env, j_object_); if (!connection.is_null()) { device_handle = UsbDeviceHandleAndroid::Create(env, this, connection); handles().push_back(device_handle.get()); } } base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::Bind(callback, device_handle)); } bool UsbDeviceAndroid::permission_granted() const { return permission_granted_; } UsbDeviceAndroid::UsbDeviceAndroid( JNIEnv* env, base::WeakPtr service, uint16_t usb_version, uint8_t device_class, uint8_t device_subclass, uint8_t device_protocol, uint16_t vendor_id, uint16_t product_id, uint16_t device_version, const base::string16& manufacturer_string, const base::string16& product_string, const base::string16& serial_number, const JavaRef& wrapper) : UsbDevice(usb_version, device_class, device_subclass, device_protocol, vendor_id, product_id, device_version, manufacturer_string, product_string, serial_number), device_id_(Java_ChromeUsbDevice_getDeviceId(env, wrapper)), service_(service), j_object_(wrapper) { if (base::android::BuildInfo::GetInstance()->sdk_int() >= 21) { ScopedJavaLocalRef configurations = Java_ChromeUsbDevice_getConfigurations(env, j_object_); jsize count = env->GetArrayLength(configurations.obj()); descriptor_.configurations.reserve(count); for (jsize i = 0; i < count; ++i) { ScopedJavaLocalRef config( env, env->GetObjectArrayElement(configurations.obj(), i)); descriptor_.configurations.push_back( UsbConfigurationAndroid::Convert(env, config)); } } else { // Pre-lollipop only the first configuration was supported. Build a basic // configuration out of the available interfaces. UsbConfigDescriptor config(1, // Configuration value, reasonable guess. false, // Self powered, arbitrary default. false, // Remote wakeup, rbitrary default. 0); // Maximum power, aitrary default. ScopedJavaLocalRef interfaces = Java_ChromeUsbDevice_getInterfaces(env, wrapper); jsize count = env->GetArrayLength(interfaces.obj()); config.interfaces.reserve(count); for (jsize i = 0; i < count; ++i) { ScopedJavaLocalRef interface( env, env->GetObjectArrayElement(interfaces.obj(), i)); config.interfaces.push_back(UsbInterfaceAndroid::Convert(env, interface)); } descriptor_.configurations.push_back(config); } if (configurations().size() > 0) ActiveConfigurationChanged(configurations()[0].configuration_value); } UsbDeviceAndroid::~UsbDeviceAndroid() {} void UsbDeviceAndroid::PermissionGranted(bool granted) { if (granted) Open(base::Bind(&UsbDeviceAndroid::OnDeviceOpenedToReadDescriptors, this)); else CallRequestPermissionCallbacks(granted); } void UsbDeviceAndroid::CallRequestPermissionCallbacks(bool granted) { permission_granted_ = granted; std::list callbacks; callbacks.swap(request_permission_callbacks_); for (const auto& callback : callbacks) callback.Run(granted); } void UsbDeviceAndroid::OnDeviceOpenedToReadDescriptors( scoped_refptr device_handle) { if (device_handle) { ReadUsbDescriptors( device_handle, base::Bind(&UsbDeviceAndroid::OnReadDescriptors, this, device_handle)); } else { CallRequestPermissionCallbacks(false); } } void UsbDeviceAndroid::OnReadDescriptors( scoped_refptr device_handle, std::unique_ptr descriptor) { if (!descriptor) { device_handle->Close(); CallRequestPermissionCallbacks(false); return; } descriptor_ = *descriptor; if (usb_version() >= 0x0210) { ReadWebUsbDescriptors(device_handle, base::Bind(&UsbDeviceAndroid::OnReadWebUsbDescriptors, this, device_handle)); } else { device_handle->Close(); CallRequestPermissionCallbacks(true); } } void UsbDeviceAndroid::OnReadWebUsbDescriptors( scoped_refptr device_handle, const GURL& landing_page) { if (landing_page.is_valid()) webusb_landing_page_ = landing_page; device_handle->Close(); CallRequestPermissionCallbacks(true); } } // namespace device