diff options
12 files changed, 360 insertions, 332 deletions
diff --git a/services/core/jni/com_android_server_UsbDescriptorParser.cpp b/services/core/jni/com_android_server_UsbDescriptorParser.cpp index df85e318619a..35e65bc3110f 100644 --- a/services/core/jni/com_android_server_UsbDescriptorParser.cpp +++ b/services/core/jni/com_android_server_UsbDescriptorParser.cpp @@ -17,16 +17,18 @@ #define LOG_TAG "UsbHostManagerJNI" #include "utils/Log.h" +#include <stdlib.h> + #include "jni.h" #include <nativehelper/JNIHelp.h> #include <usbhost/usbhost.h> -#define MAX_DESCRIPTORS_LENGTH 16384 +#define MAX_DESCRIPTORS_LENGTH 4096 // com.android.server.usb.descriptors extern "C" { -jbyteArray JNICALL Java_com_android_server_usb_descriptors_UsbDescriptorParser_getRawDescriptors( +jbyteArray JNICALL Java_com_android_server_usb_descriptors_UsbDescriptorParser_getRawDescriptors_1native( JNIEnv* env, jobject thiz, jstring deviceAddr) { const char *deviceAddrStr = env->GetStringUTFChars(deviceAddr, NULL); struct usb_device* device = usb_device_open(deviceAddrStr); @@ -39,6 +41,7 @@ jbyteArray JNICALL Java_com_android_server_usb_descriptors_UsbDescriptorParser_g int fd = usb_device_get_fd(device); if (fd < 0) { + usb_device_close(device); return NULL; } @@ -46,17 +49,48 @@ jbyteArray JNICALL Java_com_android_server_usb_descriptors_UsbDescriptorParser_g jbyte buffer[MAX_DESCRIPTORS_LENGTH]; lseek(fd, 0, SEEK_SET); int numBytes = read(fd, buffer, sizeof(buffer)); - + jbyteArray ret = NULL; usb_device_close(device); - jbyteArray ret = NULL; - if (numBytes != 0) { + if (numBytes > 0) { ret = env->NewByteArray(numBytes); env->SetByteArrayRegion(ret, 0, numBytes, buffer); + } else { + ALOGE("error reading descriptors\n"); } + return ret; } +jstring JNICALL Java_com_android_server_usb_descriptors_UsbDescriptorParser_getDescriptorString_1native( + JNIEnv* env, jobject thiz, jstring deviceAddr, jint stringId) { + + const char *deviceAddrStr = env->GetStringUTFChars(deviceAddr, NULL); + struct usb_device* device = usb_device_open(deviceAddrStr); + env->ReleaseStringUTFChars(deviceAddr, deviceAddrStr); + + if (!device) { + ALOGE("usb_device_open failed"); + return NULL; + } + + int fd = usb_device_get_fd(device); + if (fd < 0) { + ALOGE("usb_device_get_fd failed"); + usb_device_close(device); + return NULL; + } + + char* c_str = usb_device_get_string(device, stringId, 0 /*timeout*/); + + jstring j_str = env->NewStringUTF(c_str); + + free(c_str); + usb_device_close(device); + + return j_str; +} + } // extern "C" diff --git a/services/core/jni/com_android_server_UsbHostManager.cpp b/services/core/jni/com_android_server_UsbHostManager.cpp index 88ae824c1447..11f508bbf79d 100644 --- a/services/core/jni/com_android_server_UsbHostManager.cpp +++ b/services/core/jni/com_android_server_UsbHostManager.cpp @@ -22,8 +22,6 @@ #include "android_runtime/AndroidRuntime.h" #include "android_runtime/Log.h" -#include <usbhost/usbhost.h> - #include <stdio.h> #include <asm/byteorder.h> #include <sys/types.h> @@ -31,22 +29,20 @@ #include <fcntl.h> #include <sys/ioctl.h> +#include <usbhost/usbhost.h> + +#define MAX_DESCRIPTORS_LENGTH 4096 + namespace android { -static const int USB_CONTROL_TRANSFER_TIMEOUT_MS = 200; - static struct parcel_file_descriptor_offsets_t { jclass mClass; jmethodID mConstructor; } gParcelFileDescriptorOffsets; -static jmethodID method_beginUsbDeviceAdded; -static jmethodID method_addUsbConfiguration; -static jmethodID method_addUsbInterface; -static jmethodID method_addUsbEndpoint; -static jmethodID method_endUsbDeviceAdded; +static jmethodID method_usbDeviceAdded; static jmethodID method_usbDeviceRemoved; static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) { @@ -57,101 +53,63 @@ static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodNa } } -static int usb_device_added(const char *devname, void* client_data) { - struct usb_descriptor_header* desc; - struct usb_descriptor_iter iter; - - struct usb_device *device = usb_device_open(devname); +static int usb_device_added(const char *devAddress, void* clientData) { + struct usb_device *device = usb_device_open(devAddress); if (!device) { ALOGE("usb_device_open failed\n"); return 0; } - JNIEnv* env = AndroidRuntime::getJNIEnv(); - jobject thiz = (jobject)client_data; const usb_device_descriptor* deviceDesc = usb_device_get_device_descriptor(device); + int classID = deviceDesc->bDeviceClass; + int subClassID = deviceDesc->bDeviceSubClass; - char *manufacturer = usb_device_get_manufacturer_name(device, - USB_CONTROL_TRANSFER_TIMEOUT_MS); - char *product = usb_device_get_product_name(device, - USB_CONTROL_TRANSFER_TIMEOUT_MS); - int version = usb_device_get_version(device); - char *serial = usb_device_get_serial(device, - USB_CONTROL_TRANSFER_TIMEOUT_MS); - - jstring deviceName = env->NewStringUTF(devname); - jstring manufacturerName = AndroidRuntime::NewStringLatin1(env, manufacturer); - jstring productName = AndroidRuntime::NewStringLatin1(env, product); - jstring serialNumber = AndroidRuntime::NewStringLatin1(env, serial); - - jboolean result = env->CallBooleanMethod(thiz, method_beginUsbDeviceAdded, - deviceName, usb_device_get_vendor_id(device), usb_device_get_product_id(device), - deviceDesc->bDeviceClass, deviceDesc->bDeviceSubClass, deviceDesc->bDeviceProtocol, - manufacturerName, productName, version, serialNumber); - - env->DeleteLocalRef(serialNumber); - env->DeleteLocalRef(productName); - env->DeleteLocalRef(manufacturerName); - env->DeleteLocalRef(deviceName); - free(manufacturer); - free(product); - free(serial); - - if (!result) goto fail; - - usb_descriptor_iter_init(device, &iter); - - while ((desc = usb_descriptor_iter_next(&iter)) != NULL) { - if (desc->bDescriptorType == USB_DT_CONFIG) { - struct usb_config_descriptor *config = (struct usb_config_descriptor *)desc; - char *name = usb_device_get_string(device, config->iConfiguration, - USB_CONTROL_TRANSFER_TIMEOUT_MS); - jstring configName = AndroidRuntime::NewStringLatin1(env, name); - - env->CallVoidMethod(thiz, method_addUsbConfiguration, - config->bConfigurationValue, configName, config->bmAttributes, - config->bMaxPower); - - env->DeleteLocalRef(configName); - free(name); - } else if (desc->bDescriptorType == USB_DT_INTERFACE) { - struct usb_interface_descriptor *interface = (struct usb_interface_descriptor *)desc; - char *name = usb_device_get_string(device, interface->iInterface, - USB_CONTROL_TRANSFER_TIMEOUT_MS); - jstring interfaceName = AndroidRuntime::NewStringLatin1(env, name); - - env->CallVoidMethod(thiz, method_addUsbInterface, - interface->bInterfaceNumber, interfaceName, interface->bAlternateSetting, - interface->bInterfaceClass, interface->bInterfaceSubClass, - interface->bInterfaceProtocol); - - env->DeleteLocalRef(interfaceName); - free(name); - } else if (desc->bDescriptorType == USB_DT_ENDPOINT) { - struct usb_endpoint_descriptor *endpoint = (struct usb_endpoint_descriptor *)desc; - - env->CallVoidMethod(thiz, method_addUsbEndpoint, - endpoint->bEndpointAddress, endpoint->bmAttributes, - __le16_to_cpu(endpoint->wMaxPacketSize), endpoint->bInterval); - } + // get the raw descriptors + int fd = usb_device_get_fd(device); + if (fd < 0) { + ALOGE("usb_device_get_fd failed\n"); + usb_device_close(device); + // TODO return an error code here? + return 0; } - env->CallVoidMethod(thiz, method_endUsbDeviceAdded); + // from android_hardware_UsbDeviceConnection_get_desc() + jbyte rawdescriptors[MAX_DESCRIPTORS_LENGTH]; + lseek(fd, 0, SEEK_SET); + int numBytes = read(fd, rawdescriptors, sizeof(rawdescriptors)); -fail: usb_device_close(device); - checkAndClearExceptionFromCallback(env, __FUNCTION__); + + if (numBytes > 0) { + JNIEnv* env = AndroidRuntime::getJNIEnv(); + jobject thiz = (jobject)clientData; + jstring deviceAddress = env->NewStringUTF(devAddress); + + jbyteArray descriptorsArray = env->NewByteArray(numBytes); + env->SetByteArrayRegion(descriptorsArray, 0, numBytes, rawdescriptors); + + env->CallBooleanMethod(thiz, method_usbDeviceAdded, + deviceAddress, classID, subClassID, descriptorsArray); + + env->DeleteLocalRef(descriptorsArray); + env->DeleteLocalRef(deviceAddress); + + checkAndClearExceptionFromCallback(env, __FUNCTION__); + } else { + // TODO return an error code here? + ALOGE("error reading descriptors\n"); + } return 0; } -static int usb_device_removed(const char *devname, void* client_data) { +static int usb_device_removed(const char *devAddress, void* clientData) { JNIEnv* env = AndroidRuntime::getJNIEnv(); - jobject thiz = (jobject)client_data; + jobject thiz = (jobject)clientData; - jstring deviceName = env->NewStringUTF(devname); - env->CallVoidMethod(thiz, method_usbDeviceRemoved, deviceName); - env->DeleteLocalRef(deviceName); + jstring deviceAddress = env->NewStringUTF(devAddress); + env->CallVoidMethod(thiz, method_usbDeviceRemoved, deviceAddress); + env->DeleteLocalRef(deviceAddress); checkAndClearExceptionFromCallback(env, __FUNCTION__); return 0; } @@ -168,11 +126,11 @@ static void android_server_UsbHostManager_monitorUsbHostBus(JNIEnv* /* env */, j } static jobject android_server_UsbHostManager_openDevice(JNIEnv *env, jobject /* thiz */, - jstring deviceName) + jstring deviceAddress) { - const char *deviceNameStr = env->GetStringUTFChars(deviceName, NULL); - struct usb_device* device = usb_device_open(deviceNameStr); - env->ReleaseStringUTFChars(deviceName, deviceNameStr); + const char *deviceAddressStr = env->GetStringUTFChars(deviceAddress, NULL); + struct usb_device* device = usb_device_open(deviceAddressStr); + env->ReleaseStringUTFChars(deviceAddress, deviceAddressStr); if (!device) return NULL; @@ -206,34 +164,12 @@ int register_android_server_UsbHostManager(JNIEnv *env) ALOGE("Can't find com/android/server/usb/UsbHostManager"); return -1; } - method_beginUsbDeviceAdded = env->GetMethodID(clazz, "beginUsbDeviceAdded", - "(Ljava/lang/String;IIIIILjava/lang/String;Ljava/lang/String;ILjava/lang/String;)Z"); - if (method_beginUsbDeviceAdded == NULL) { + method_usbDeviceAdded = + env->GetMethodID(clazz, "usbDeviceAdded", "(Ljava/lang/String;II[B)Z"); + if (method_usbDeviceAdded == NULL) { ALOGE("Can't find beginUsbDeviceAdded"); return -1; } - method_addUsbConfiguration = env->GetMethodID(clazz, "addUsbConfiguration", - "(ILjava/lang/String;II)V"); - if (method_addUsbConfiguration == NULL) { - ALOGE("Can't find addUsbConfiguration"); - return -1; - } - method_addUsbInterface = env->GetMethodID(clazz, "addUsbInterface", - "(ILjava/lang/String;IIII)V"); - if (method_addUsbInterface == NULL) { - ALOGE("Can't find addUsbInterface"); - return -1; - } - method_addUsbEndpoint = env->GetMethodID(clazz, "addUsbEndpoint", "(IIII)V"); - if (method_addUsbEndpoint == NULL) { - ALOGE("Can't find addUsbEndpoint"); - return -1; - } - method_endUsbDeviceAdded = env->GetMethodID(clazz, "endUsbDeviceAdded", "()V"); - if (method_endUsbDeviceAdded == NULL) { - ALOGE("Can't find endUsbDeviceAdded"); - return -1; - } method_usbDeviceRemoved = env->GetMethodID(clazz, "usbDeviceRemoved", "(Ljava/lang/String;)V"); if (method_usbDeviceRemoved == NULL) { diff --git a/services/usb/java/com/android/server/usb/UsbAlsaManager.java b/services/usb/java/com/android/server/usb/UsbAlsaManager.java index d359b7045007..7bea8a11133b 100644 --- a/services/usb/java/com/android/server/usb/UsbAlsaManager.java +++ b/services/usb/java/com/android/server/usb/UsbAlsaManager.java @@ -255,6 +255,7 @@ public final class UsbAlsaManager { } private void alsaFileAdded(String name) { + Slog.i(TAG, "alsaFileAdded(" + name + ")"); int type = AlsaDevice.TYPE_UNKNOWN; int card = -1, device = -1; diff --git a/services/usb/java/com/android/server/usb/UsbHostManager.java b/services/usb/java/com/android/server/usb/UsbHostManager.java index 095fdc63975c..c29a3359ace7 100644 --- a/services/usb/java/com/android/server/usb/UsbHostManager.java +++ b/services/usb/java/com/android/server/usb/UsbHostManager.java @@ -19,13 +19,8 @@ package com.android.server.usb; import android.annotation.Nullable; import android.content.ComponentName; import android.content.Context; -import android.hardware.usb.UsbConfiguration; import android.hardware.usb.UsbConstants; import android.hardware.usb.UsbDevice; -import android.hardware.usb.UsbDeviceConnection; -import android.hardware.usb.UsbEndpoint; -import android.hardware.usb.UsbInterface; -import android.hardware.usb.UsbManager; import android.os.Bundle; import android.os.ParcelFileDescriptor; import android.text.TextUtils; @@ -37,7 +32,6 @@ import com.android.server.usb.descriptors.UsbDescriptorParser; import com.android.server.usb.descriptors.report.TextReportCanvas; import com.android.server.usb.descriptors.tree.UsbDescriptorsTree; -import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; @@ -50,28 +44,23 @@ public class UsbHostManager { private final Context mContext; - // contains all connected USB devices - private final HashMap<String, UsbDevice> mDevices = new HashMap<>(); - // USB busses to exclude from USB host support private final String[] mHostBlacklist; - private final Object mLock = new Object(); - - private UsbDevice mNewDevice; - private UsbConfiguration mNewConfiguration; - private UsbInterface mNewInterface; - private ArrayList<UsbConfiguration> mNewConfigurations; - private ArrayList<UsbInterface> mNewInterfaces; - private ArrayList<UsbEndpoint> mNewEndpoints; - private final UsbAlsaManager mUsbAlsaManager; private final UsbSettingsManager mSettingsManager; + private final Object mLock = new Object(); @GuardedBy("mLock") + // contains all connected USB devices + private final HashMap<String, UsbDevice> mDevices = new HashMap<>(); + + private Object mSettingsLock = new Object(); + @GuardedBy("mSettingsLock") private UsbProfileGroupSettingsManager mCurrentSettings; - @GuardedBy("mLock") + private Object mHandlerLock = new Object(); + @GuardedBy("mHandlerLock") private ComponentName mUsbDeviceConnectionHandler; public UsbHostManager(Context context, UsbAlsaManager alsaManager, @@ -91,33 +80,33 @@ public class UsbHostManager { } public void setCurrentUserSettings(UsbProfileGroupSettingsManager settings) { - synchronized (mLock) { + synchronized (mSettingsLock) { mCurrentSettings = settings; } } private UsbProfileGroupSettingsManager getCurrentUserSettings() { - synchronized (mLock) { + synchronized (mSettingsLock) { return mCurrentSettings; } } public void setUsbDeviceConnectionHandler(@Nullable ComponentName usbDeviceConnectionHandler) { - synchronized (mLock) { + synchronized (mHandlerLock) { mUsbDeviceConnectionHandler = usbDeviceConnectionHandler; } } private @Nullable ComponentName getUsbDeviceConnectionHandler() { - synchronized (mLock) { + synchronized (mHandlerLock) { return mUsbDeviceConnectionHandler; } } - private boolean isBlackListed(String deviceName) { + private boolean isBlackListed(String deviceAddress) { int count = mHostBlacklist.length; for (int i = 0; i < count; i++) { - if (deviceName.startsWith(mHostBlacklist[i])) { + if (deviceAddress.startsWith(mHostBlacklist[i])) { return true; } } @@ -136,166 +125,73 @@ public class UsbHostManager { } /* Called from JNI in monitorUsbHostBus() to report new USB devices - Returns true if successful, in which case the JNI code will continue adding configurations, - interfaces and endpoints, and finally call endUsbDeviceAdded after all descriptors - have been processed + Returns true if successful, i.e. the USB Audio device descriptors are + correctly parsed and the unique device is added to the audio device list. */ @SuppressWarnings("unused") - private boolean beginUsbDeviceAdded(String deviceName, int vendorID, int productID, - int deviceClass, int deviceSubclass, int deviceProtocol, - String manufacturerName, String productName, int version, String serialNumber) { - + private boolean usbDeviceAdded(String deviceAddress, int deviceClass, int deviceSubclass, + byte[] descriptors) { if (DEBUG) { - Slog.d(TAG, "usb:UsbHostManager.beginUsbDeviceAdded(" + deviceName + ")"); - // Audio Class Codes: - // Audio: 0x01 - // Audio Subclass Codes: - // undefined: 0x00 - // audio control: 0x01 - // audio streaming: 0x02 - // midi streaming: 0x03 - - // some useful debugging info - Slog.d(TAG, "usb: nm:" + deviceName + " vnd:" + vendorID + " prd:" + productID + " cls:" - + deviceClass + " sub:" + deviceSubclass + " proto:" + deviceProtocol); + Slog.d(TAG, "usbDeviceAdded(" + deviceAddress + ") - start"); } - // OK this is non-obvious, but true. One can't tell if the device being attached is even - // potentially an audio device without parsing the interface descriptors, so punt on any - // such test until endUsbDeviceAdded() when we have that info. - - if (isBlackListed(deviceName) || - isBlackListed(deviceClass, deviceSubclass)) { + // check class/subclass first as it is more likely to be blacklisted + if (isBlackListed(deviceClass, deviceSubclass) || isBlackListed(deviceAddress)) { + if (DEBUG) { + Slog.d(TAG, "device is black listed"); + } return false; } synchronized (mLock) { - if (mDevices.get(deviceName) != null) { - Slog.w(TAG, "device already on mDevices list: " + deviceName); + if (mDevices.get(deviceAddress) != null) { + Slog.w(TAG, "device already on mDevices list: " + deviceAddress); + //TODO If this is the same peripheral as is being connected, replace + // it with the new connection. return false; } - if (mNewDevice != null) { - Slog.e(TAG, "mNewDevice is not null in endUsbDeviceAdded"); - return false; - } - - // Create version string in "%.%" format - String versionString = Integer.toString(version >> 8) + "." + (version & 0xFF); + UsbDescriptorParser parser = new UsbDescriptorParser(deviceAddress); + if (parser.parseDescriptors(descriptors)) { - mNewDevice = new UsbDevice(deviceName, vendorID, productID, - deviceClass, deviceSubclass, deviceProtocol, - manufacturerName, productName, versionString, serialNumber); - - mNewConfigurations = new ArrayList<>(); - mNewInterfaces = new ArrayList<>(); - mNewEndpoints = new ArrayList<>(); - } - - return true; - } - - /* Called from JNI in monitorUsbHostBus() to report new USB configuration for the device - currently being added. Returns true if successful, false in case of error. - */ - @SuppressWarnings("unused") - private void addUsbConfiguration(int id, String name, int attributes, int maxPower) { - if (mNewConfiguration != null) { - mNewConfiguration.setInterfaces( - mNewInterfaces.toArray(new UsbInterface[mNewInterfaces.size()])); - mNewInterfaces.clear(); - } - - mNewConfiguration = new UsbConfiguration(id, name, attributes, maxPower); - mNewConfigurations.add(mNewConfiguration); - } - - /* Called from JNI in monitorUsbHostBus() to report new USB interface for the device - currently being added. Returns true if successful, false in case of error. - */ - @SuppressWarnings("unused") - private void addUsbInterface(int id, String name, int altSetting, - int Class, int subClass, int protocol) { - if (mNewInterface != null) { - mNewInterface.setEndpoints( - mNewEndpoints.toArray(new UsbEndpoint[mNewEndpoints.size()])); - mNewEndpoints.clear(); - } - - mNewInterface = new UsbInterface(id, altSetting, name, Class, subClass, protocol); - mNewInterfaces.add(mNewInterface); - } - - /* Called from JNI in monitorUsbHostBus() to report new USB endpoint for the device - currently being added. Returns true if successful, false in case of error. - */ - @SuppressWarnings("unused") - private void addUsbEndpoint(int address, int attributes, int maxPacketSize, int interval) { - mNewEndpoints.add(new UsbEndpoint(address, attributes, maxPacketSize, interval)); - } - - /* Called from JNI in monitorUsbHostBus() to finish adding a new device */ - @SuppressWarnings("unused") - private void endUsbDeviceAdded() { - if (DEBUG) { - Slog.d(TAG, "usb:UsbHostManager.endUsbDeviceAdded()"); - } - if (mNewInterface != null) { - mNewInterface.setEndpoints( - mNewEndpoints.toArray(new UsbEndpoint[mNewEndpoints.size()])); - } - if (mNewConfiguration != null) { - mNewConfiguration.setInterfaces( - mNewInterfaces.toArray(new UsbInterface[mNewInterfaces.size()])); - } - - - synchronized (mLock) { - if (mNewDevice != null) { - mNewDevice.setConfigurations( - mNewConfigurations.toArray( - new UsbConfiguration[mNewConfigurations.size()])); - mDevices.put(mNewDevice.getDeviceName(), mNewDevice); - Slog.d(TAG, "Added device " + mNewDevice); + UsbDevice newDevice = parser.toAndroidUsbDevice(); + mDevices.put(deviceAddress, newDevice); // It is fine to call this only for the current user as all broadcasts are sent to // all profiles of the user and the dialogs should only show once. ComponentName usbDeviceConnectionHandler = getUsbDeviceConnectionHandler(); if (usbDeviceConnectionHandler == null) { - getCurrentUserSettings().deviceAttached(mNewDevice); + getCurrentUserSettings().deviceAttached(newDevice); } else { - getCurrentUserSettings().deviceAttachedForFixedHandler(mNewDevice, + getCurrentUserSettings().deviceAttachedForFixedHandler(newDevice, usbDeviceConnectionHandler); } - // deviceName is something like: "/dev/bus/usb/001/001" - UsbDescriptorParser parser = new UsbDescriptorParser(); - boolean isInputHeadset = false; - boolean isOutputHeadset = false; - if (parser.parseDevice(mNewDevice.getDeviceName())) { - isInputHeadset = parser.isInputHeadset(); - isOutputHeadset = parser.isOutputHeadset(); - Slog.i(TAG, "---- isHeadset[in: " + isInputHeadset - + " , out: " + isOutputHeadset + "]"); - } - mUsbAlsaManager.usbDeviceAdded(mNewDevice, - isInputHeadset, isOutputHeadset); + + // Headset? + boolean isInputHeadset = parser.isInputHeadset(); + boolean isOutputHeadset = parser.isOutputHeadset(); + Slog.i(TAG, "---- isHeadset[in: " + isInputHeadset + + " , out: " + isOutputHeadset + "]"); + + mUsbAlsaManager.usbDeviceAdded(newDevice, isInputHeadset, isOutputHeadset); } else { - Slog.e(TAG, "mNewDevice is null in endUsbDeviceAdded"); + Slog.e(TAG, "Error parsing USB device descriptors for " + deviceAddress); + return false; } - mNewDevice = null; - mNewConfigurations = null; - mNewInterfaces = null; - mNewEndpoints = null; - mNewConfiguration = null; - mNewInterface = null; } + + if (DEBUG) { + Slog.d(TAG, "beginUsbDeviceAdded(" + deviceAddress + ") end"); + } + + return true; } /* Called from JNI in monitorUsbHostBus to report USB device removal */ @SuppressWarnings("unused") - private void usbDeviceRemoved(String deviceName) { + private void usbDeviceRemoved(String deviceAddress) { synchronized (mLock) { - UsbDevice device = mDevices.remove(deviceName); + UsbDevice device = mDevices.remove(deviceAddress); if (device != null) { mUsbAlsaManager.usbDeviceRemoved(device); mSettingsManager.usbDeviceRemoved(device); @@ -322,32 +218,37 @@ public class UsbHostManager { } } - /* Opens the specified USB device */ - public ParcelFileDescriptor openDevice(String deviceName, UsbUserSettingsManager settings) { + /** + * Opens the specified USB device + * @hide + */ + public ParcelFileDescriptor openDevice(String deviceAddress, UsbUserSettingsManager settings) { synchronized (mLock) { - if (isBlackListed(deviceName)) { + if (isBlackListed(deviceAddress)) { throw new SecurityException("USB device is on a restricted bus"); } - UsbDevice device = mDevices.get(deviceName); + UsbDevice device = mDevices.get(deviceAddress); if (device == null) { // if it is not in mDevices, it either does not exist or is blacklisted throw new IllegalArgumentException( - "device " + deviceName + " does not exist or is restricted"); + "device " + deviceAddress + " does not exist or is restricted"); } settings.checkPermission(device); - return nativeOpenDevice(deviceName); + return nativeOpenDevice(deviceAddress); } } public void dump(IndentingPrintWriter pw) { + pw.println("USB Host State:"); + synchronized (mHandlerLock) { + if (mUsbDeviceConnectionHandler != null) { + pw.println("Default USB Host Connection handler: " + mUsbDeviceConnectionHandler); + } + } synchronized (mLock) { - pw.println("USB Host State:"); for (String name : mDevices.keySet()) { pw.println(" " + name + ": " + mDevices.get(name)); } - if (mUsbDeviceConnectionHandler != null) { - pw.println("Default USB Host Connection handler: " + mUsbDeviceConnectionHandler); - } Collection<UsbDevice> devices = mDevices.values(); if (devices.size() != 0) { @@ -355,17 +256,12 @@ public class UsbHostManager { for (UsbDevice device : devices) { StringBuilder stringBuilder = new StringBuilder(); - UsbDescriptorParser parser = new UsbDescriptorParser(); - if (parser.parseDevice(device.getDeviceName())) { + UsbDescriptorParser parser = new UsbDescriptorParser(device.getDeviceName()); + if (parser.parseDevice()) { UsbDescriptorsTree descriptorTree = new UsbDescriptorsTree(); descriptorTree.parse(parser); - UsbManager usbManager = - (UsbManager) mContext.getSystemService(Context.USB_SERVICE); - UsbDeviceConnection connection = usbManager.openDevice(device); - - descriptorTree.report(new TextReportCanvas(connection, stringBuilder)); - connection.close(); + descriptorTree.report(new TextReportCanvas(parser, stringBuilder)); stringBuilder.append("isHeadset[in: " + parser.isInputHeadset() + " , out: " + parser.isOutputHeadset() + "]"); @@ -381,5 +277,5 @@ public class UsbHostManager { } private native void monitorUsbHostBus(); - private native ParcelFileDescriptor nativeOpenDevice(String deviceName); + private native ParcelFileDescriptor nativeOpenDevice(String deviceAddress); } diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbConfigDescriptor.java b/services/usb/java/com/android/server/usb/descriptors/UsbConfigDescriptor.java index 75279c61c4f0..511e59a05b63 100644 --- a/services/usb/java/com/android/server/usb/descriptors/UsbConfigDescriptor.java +++ b/services/usb/java/com/android/server/usb/descriptors/UsbConfigDescriptor.java @@ -15,8 +15,13 @@ */ package com.android.server.usb.descriptors; +import android.hardware.usb.UsbConfiguration; +import android.hardware.usb.UsbInterface; + import com.android.server.usb.descriptors.report.ReportCanvas; +import java.util.ArrayList; + /** * @hide * An USB Config Descriptor. @@ -35,6 +40,9 @@ public final class UsbConfigDescriptor extends UsbDescriptor { // D4..0 Reserved, set to 0. private byte mMaxPower; // 8:1 Maximum Power Consumption in 2mA units + private ArrayList<UsbInterfaceDescriptor> mInterfaceDescriptors = + new ArrayList<UsbInterfaceDescriptor>(); + UsbConfigDescriptor(int length, byte type) { super(length, type); mHierarchyLevel = 2; @@ -64,6 +72,22 @@ public final class UsbConfigDescriptor extends UsbDescriptor { return mMaxPower; } + void addInterfaceDescriptor(UsbInterfaceDescriptor interfaceDesc) { + mInterfaceDescriptors.add(interfaceDesc); + } + + UsbConfiguration toAndroid(UsbDescriptorParser parser) { + String name = parser.getDescriptorString(mConfigIndex); + UsbConfiguration config = new + UsbConfiguration(mConfigValue, name, mAttribs, mMaxPower); + UsbInterface[] interfaces = new UsbInterface[mInterfaceDescriptors.size()]; + for (int index = 0; index < mInterfaceDescriptors.size(); index++) { + interfaces[index] = mInterfaceDescriptors.get(index).toAndroid(parser); + } + config.setInterfaces(interfaces); + return config; + } + @Override public int parseRawDescriptors(ByteStream stream) { mTotalLength = stream.unpackUsbShort(); diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java index ad7bde5c275e..c5052c8fc26d 100644 --- a/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java +++ b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java @@ -15,6 +15,7 @@ */ package com.android.server.usb.descriptors; +import android.hardware.usb.UsbDevice; import android.util.Log; import java.util.ArrayList; @@ -25,11 +26,16 @@ import java.util.ArrayList; */ public final class UsbDescriptorParser { private static final String TAG = "UsbDescriptorParser"; + private static final boolean DEBUG = false; + + private final String mDeviceAddr; // Descriptor Objects + private static final int DESCRIPTORS_ALLOC_SIZE = 128; private ArrayList<UsbDescriptor> mDescriptors = new ArrayList<UsbDescriptor>(); private UsbDeviceDescriptor mDeviceDescriptor; + private UsbConfigDescriptor mCurConfigDescriptor; private UsbInterfaceDescriptor mCurInterfaceDescriptor; // The AudioClass spec implemented by the AudioClass Interfaces @@ -37,7 +43,13 @@ public final class UsbDescriptorParser { // Obtained from the first AudioClass Header descriptor. private int mACInterfacesSpec = UsbDeviceDescriptor.USBSPEC_1_0; - public UsbDescriptorParser() {} + public UsbDescriptorParser(String deviceAddr) { + mDeviceAddr = deviceAddr; + } + + public String getDeviceAddr() { + return mDeviceAddr; + } /** * @return the USB Spec value associated with the Device descriptor for the @@ -60,6 +72,18 @@ public final class UsbDescriptorParser { public int getACInterfaceSpec() { return mACInterfacesSpec; } + + private class UsbDescriptorsStreamFormatException extends Exception { + String mMessage; + UsbDescriptorsStreamFormatException(String message) { + mMessage = message; + } + + public String toString() { + return "Descriptor Stream Format Exception: " + mMessage; + } + } + /** * The probability (as returned by getHeadsetProbability() at which we conclude * the peripheral is a headset. @@ -67,7 +91,8 @@ public final class UsbDescriptorParser { private static final float IN_HEADSET_TRIGGER = 0.75f; private static final float OUT_HEADSET_TRIGGER = 0.75f; - private UsbDescriptor allocDescriptor(ByteStream stream) { + private UsbDescriptor allocDescriptor(ByteStream stream) + throws UsbDescriptorsStreamFormatException { stream.resetReadCount(); int length = stream.getUnsignedByte(); @@ -83,15 +108,38 @@ public final class UsbDescriptorParser { break; case UsbDescriptor.DESCRIPTORTYPE_CONFIG: - descriptor = new UsbConfigDescriptor(length, type); + descriptor = mCurConfigDescriptor = new UsbConfigDescriptor(length, type); + if (mDeviceDescriptor != null) { + mDeviceDescriptor.addConfigDescriptor(mCurConfigDescriptor); + } else { + Log.e(TAG, "Config Descriptor found with no associated Device Descriptor!"); + throw new UsbDescriptorsStreamFormatException( + "Config Descriptor found with no associated Device Descriptor!"); + } break; case UsbDescriptor.DESCRIPTORTYPE_INTERFACE: descriptor = mCurInterfaceDescriptor = new UsbInterfaceDescriptor(length, type); + if (mCurConfigDescriptor != null) { + mCurConfigDescriptor.addInterfaceDescriptor(mCurInterfaceDescriptor); + } else { + Log.e(TAG, "Interface Descriptor found with no associated Config Descriptor!"); + throw new UsbDescriptorsStreamFormatException( + "Interface Descriptor found with no associated Config Descriptor!"); + } break; case UsbDescriptor.DESCRIPTORTYPE_ENDPOINT: descriptor = new UsbEndpointDescriptor(length, type); + if (mCurInterfaceDescriptor != null) { + mCurInterfaceDescriptor.addEndpointDescriptor( + (UsbEndpointDescriptor) descriptor); + } else { + Log.e(TAG, + "Endpoint Descriptor found with no associated Interface Descriptor!"); + throw new UsbDescriptorsStreamFormatException( + "Endpoint Descriptor found with no associated Interface Descriptor!"); + } break; /* @@ -144,8 +192,12 @@ public final class UsbDescriptorParser { /** * @hide */ - public void parseDescriptors(byte[] descriptors) { - mDescriptors.clear(); + public boolean parseDescriptors(byte[] descriptors) { + if (DEBUG) { + Log.d(TAG, "parseDescriptors() - start"); + } + // This will allow us to (probably) alloc mDescriptors just once. + mDescriptors = new ArrayList<UsbDescriptor>(DESCRIPTORS_ALLOC_SIZE); ByteStream stream = new ByteStream(descriptors); while (stream.available() > 0) { @@ -173,21 +225,36 @@ public final class UsbDescriptorParser { } } } + if (DEBUG) { + Log.d(TAG, "parseDescriptors() - end " + mDescriptors.size() + " descriptors."); + } + return true; } /** * @hide */ - public boolean parseDevice(String deviceAddr) { - byte[] rawDescriptors = getRawDescriptors(deviceAddr); - if (rawDescriptors != null) { - parseDescriptors(rawDescriptors); - return true; - } - return false; + public boolean parseDevice() { + byte[] rawDescriptors = getRawDescriptors(); + + return rawDescriptors != null + ? parseDescriptors(rawDescriptors) : false; + } + + private byte[] getRawDescriptors() { + return getRawDescriptors_native(mDeviceAddr); + } + + private native byte[] getRawDescriptors_native(String deviceAddr); + + /** + * @hide + */ + public String getDescriptorString(int stringId) { + return getDescriptorString_native(mDeviceAddr, stringId); } - private native byte[] getRawDescriptors(String deviceAddr); + private native String getDescriptorString_native(String deviceAddr, int stringId); public int getParsingSpec() { return mDeviceDescriptor != null ? mDeviceDescriptor.getSpec() : 0; @@ -200,6 +267,17 @@ public final class UsbDescriptorParser { /** * @hide */ + public UsbDevice toAndroidUsbDevice() { + if (mDeviceDescriptor == null) { + return null; + } + + return mDeviceDescriptor.toAndroid(this); + } + + /** + * @hide + */ public ArrayList<UsbDescriptor> getDescriptors(byte type) { ArrayList<UsbDescriptor> list = new ArrayList<UsbDescriptor>(); for (UsbDescriptor descriptor : mDescriptors) { @@ -355,8 +433,6 @@ public final class UsbDescriptorParser { * to count on the peripheral being a headset. */ public boolean isInputHeadset() { - // TEMP - Log.i(TAG, "---- isInputHeadset() prob:" + (getInputHeadsetProbability() * 100f) + "%"); return getInputHeadsetProbability() >= IN_HEADSET_TRIGGER; } @@ -410,8 +486,6 @@ public final class UsbDescriptorParser { * to count on the peripheral being a headset. */ public boolean isOutputHeadset() { - // TEMP - Log.i(TAG, "---- isOutputHeadset() prob:" + (getOutputHeadsetProbability() * 100f) + "%"); return getOutputHeadsetProbability() >= OUT_HEADSET_TRIGGER; } diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbDeviceDescriptor.java b/services/usb/java/com/android/server/usb/descriptors/UsbDeviceDescriptor.java index d5cb89ea82e6..c89d9b6ab72f 100644 --- a/services/usb/java/com/android/server/usb/descriptors/UsbDeviceDescriptor.java +++ b/services/usb/java/com/android/server/usb/descriptors/UsbDeviceDescriptor.java @@ -15,9 +15,14 @@ */ package com.android.server.usb.descriptors; +import android.hardware.usb.UsbConfiguration; +import android.hardware.usb.UsbDevice; + import com.android.server.usb.descriptors.report.ReportCanvas; import com.android.server.usb.descriptors.report.UsbStrings; +import java.util.ArrayList; + /** * @hide * A USB Device Descriptor. @@ -44,6 +49,9 @@ public final class UsbDeviceDescriptor extends UsbDescriptor { private byte mSerialNum; // 16:1 Index of Serial Number String Descriptor private byte mNumConfigs; // 17:1 Number of Possible Configurations + private ArrayList<UsbConfigDescriptor> mConfigDescriptors = + new ArrayList<UsbConfigDescriptor>(); + UsbDeviceDescriptor(int length, byte type) { super(length, type); mHierarchyLevel = 1; @@ -97,6 +105,35 @@ public final class UsbDeviceDescriptor extends UsbDescriptor { return mNumConfigs; } + void addConfigDescriptor(UsbConfigDescriptor config) { + mConfigDescriptors.add(config); + } + + /** + * @hide + */ + public UsbDevice toAndroid(UsbDescriptorParser parser) { + String mfgName = parser.getDescriptorString(mMfgIndex); + String prodName = parser.getDescriptorString(mProductIndex); + + // Create version string in "%.%" format + String versionString = + Integer.toString(mDeviceRelease >> 8) + "." + (mDeviceRelease & 0xFF); + String serialStr = parser.getDescriptorString(mSerialNum); + + UsbDevice device = new UsbDevice(parser.getDeviceAddr(), mVendorID, mProductID, + mDevClass, mDevSubClass, + mProtocol, mfgName, prodName, + versionString, serialStr); + UsbConfiguration[] configs = new UsbConfiguration[mConfigDescriptors.size()]; + for (int index = 0; index < mConfigDescriptors.size(); index++) { + configs[index] = mConfigDescriptors.get(index).toAndroid(parser); + } + device.setConfigurations(configs); + + return device; + } + @Override public int parseRawDescriptors(ByteStream stream) { mSpec = stream.unpackUsbShort(); @@ -134,12 +171,11 @@ public final class UsbDeviceDescriptor extends UsbDescriptor { + " Product ID: " + ReportCanvas.getHexString(getProductID()) + " Product Release: " + ReportCanvas.getBCDString(getDeviceRelease())); + UsbDescriptorParser parser = canvas.getParser(); byte mfgIndex = getMfgIndex(); - String manufacturer = - UsbDescriptor.getUsbDescriptorString(canvas.getConnection(), mfgIndex); + String manufacturer = parser.getDescriptorString(mfgIndex); byte productIndex = getProductIndex(); - String product = - UsbDescriptor.getUsbDescriptorString(canvas.getConnection(), productIndex); + String product = parser.getDescriptorString(productIndex); canvas.writeListItem("Manufacturer " + mfgIndex + ": " + manufacturer + " Product " + productIndex + ": " + product); diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbEndpointDescriptor.java b/services/usb/java/com/android/server/usb/descriptors/UsbEndpointDescriptor.java index 6322fbe8b45b..9cb8f056928c 100644 --- a/services/usb/java/com/android/server/usb/descriptors/UsbEndpointDescriptor.java +++ b/services/usb/java/com/android/server/usb/descriptors/UsbEndpointDescriptor.java @@ -15,6 +15,8 @@ */ package com.android.server.usb.descriptors; +import android.hardware.usb.UsbEndpoint; + import com.android.server.usb.descriptors.report.ReportCanvas; /** @@ -105,6 +107,10 @@ public class UsbEndpointDescriptor extends UsbDescriptor { return mSyncAddress; } + /* package */ UsbEndpoint toAndroid(UsbDescriptorParser parser) { + return new UsbEndpoint(mEndpointAddress, mAttributes, mPacketSize, mInterval); + } + @Override public int parseRawDescriptors(ByteStream stream) { mEndpointAddress = stream.getByte(); diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbInterfaceDescriptor.java b/services/usb/java/com/android/server/usb/descriptors/UsbInterfaceDescriptor.java index 4eef6caf5a60..645807d2309e 100644 --- a/services/usb/java/com/android/server/usb/descriptors/UsbInterfaceDescriptor.java +++ b/services/usb/java/com/android/server/usb/descriptors/UsbInterfaceDescriptor.java @@ -15,9 +15,14 @@ */ package com.android.server.usb.descriptors; +import android.hardware.usb.UsbEndpoint; +import android.hardware.usb.UsbInterface; + import com.android.server.usb.descriptors.report.ReportCanvas; import com.android.server.usb.descriptors.report.UsbStrings; +import java.util.ArrayList; + /** * @hide * A common super-class for all USB Interface Descritor subtypes. @@ -34,6 +39,9 @@ public class UsbInterfaceDescriptor extends UsbDescriptor { protected byte mProtocol; // 7:1 Protocol Code protected byte mDescrIndex; // 8:1 Index of String Descriptor Describing this interface + private ArrayList<UsbEndpointDescriptor> mEndpointDescriptors = + new ArrayList<UsbEndpointDescriptor>(); + UsbInterfaceDescriptor(int length, byte type) { super(length, type); mHierarchyLevel = 3; @@ -80,6 +88,22 @@ public class UsbInterfaceDescriptor extends UsbDescriptor { return mDescrIndex; } + void addEndpointDescriptor(UsbEndpointDescriptor endpoint) { + mEndpointDescriptors.add(endpoint); + } + + UsbInterface toAndroid(UsbDescriptorParser parser) { + String name = parser.getDescriptorString(mDescrIndex); + UsbInterface ntrface = new UsbInterface( + mInterfaceNumber, mAlternateSetting, name, mUsbClass, mUsbSubclass, mProtocol); + UsbEndpoint[] endpoints = new UsbEndpoint[mEndpointDescriptors.size()]; + for (int index = 0; index < mEndpointDescriptors.size(); index++) { + endpoints[index] = mEndpointDescriptors.get(index).toAndroid(parser); + } + ntrface.setEndpoints(endpoints); + return ntrface; + } + @Override public void report(ReportCanvas canvas) { super.report(canvas); diff --git a/services/usb/java/com/android/server/usb/descriptors/report/HTMLReportCanvas.java b/services/usb/java/com/android/server/usb/descriptors/report/HTMLReportCanvas.java index 99ebccade735..adfc5143bc09 100644 --- a/services/usb/java/com/android/server/usb/descriptors/report/HTMLReportCanvas.java +++ b/services/usb/java/com/android/server/usb/descriptors/report/HTMLReportCanvas.java @@ -15,7 +15,7 @@ */ package com.android.server.usb.descriptors.report; -import android.hardware.usb.UsbDeviceConnection; +import com.android.server.usb.descriptors.UsbDescriptorParser; /** * @hide @@ -32,8 +32,8 @@ public final class HTMLReportCanvas extends ReportCanvas { * from the USB device. * @param stringBuilder Generated output gets written into this object. */ - public HTMLReportCanvas(UsbDeviceConnection connection, StringBuilder stringBuilder) { - super(connection); + public HTMLReportCanvas(UsbDescriptorParser parser, StringBuilder stringBuilder) { + super(parser); mStringBuilder = stringBuilder; } diff --git a/services/usb/java/com/android/server/usb/descriptors/report/ReportCanvas.java b/services/usb/java/com/android/server/usb/descriptors/report/ReportCanvas.java index 9e0adf55d87b..c34dc988555d 100644 --- a/services/usb/java/com/android/server/usb/descriptors/report/ReportCanvas.java +++ b/services/usb/java/com/android/server/usb/descriptors/report/ReportCanvas.java @@ -15,7 +15,7 @@ */ package com.android.server.usb.descriptors.report; -import android.hardware.usb.UsbDeviceConnection; +import com.android.server.usb.descriptors.UsbDescriptorParser; /** * @hide @@ -24,22 +24,19 @@ import android.hardware.usb.UsbDeviceConnection; public abstract class ReportCanvas { private static final String TAG = "ReportCanvas"; - private final UsbDeviceConnection mConnection; + private final UsbDescriptorParser mParser; /** * Constructor. * @param connection The USB connection object used to retrieve strings * from the USB device. */ - public ReportCanvas(UsbDeviceConnection connection) { - mConnection = connection; + public ReportCanvas(UsbDescriptorParser parser) { + mParser = parser; } - /** - * @returns the UsbDeviceConnection member (mConnection). - */ - public UsbDeviceConnection getConnection() { - return mConnection; + public UsbDescriptorParser getParser() { + return mParser; } /** diff --git a/services/usb/java/com/android/server/usb/descriptors/report/TextReportCanvas.java b/services/usb/java/com/android/server/usb/descriptors/report/TextReportCanvas.java index a43569d40a67..1e19ea1447ef 100644 --- a/services/usb/java/com/android/server/usb/descriptors/report/TextReportCanvas.java +++ b/services/usb/java/com/android/server/usb/descriptors/report/TextReportCanvas.java @@ -15,7 +15,7 @@ */ package com.android.server.usb.descriptors.report; -import android.hardware.usb.UsbDeviceConnection; +import com.android.server.usb.descriptors.UsbDescriptorParser; /** * @hide @@ -34,8 +34,8 @@ public final class TextReportCanvas extends ReportCanvas { * from the USB device. * @param stringBuilder Generated output gets written into this object. */ - public TextReportCanvas(UsbDeviceConnection connection, StringBuilder stringBuilder) { - super(connection); + public TextReportCanvas(UsbDescriptorParser parser, StringBuilder stringBuilder) { + super(parser); mStringBuilder = stringBuilder; } |