Added missing USB device descriptor fields needed for intent filters

The UsbDevice object is missing the ManufacturerName, ProductName, and
SerialNumber fields.  These are needed by intent filters to further
qualify a USB device that is plugged in while in host mode.  These
fields have been added in the jni UsbHostManager implementation and
propagated through UsbHostManager and UsbDevice implementations.
The UsbSettingsManager implementation has been modified to allow
manufacturer-name, product-name, and serial-number tags in intents.

File changes:
       modified:   api/current.txt
       modified:   core/java/android/hardware/usb/UsbDevice.java
       modified:   services/java/com/android/server/usb/UsbHostManager.java
       modified:   services/java/com/android/server/usb/UsbSettingsManager.java
       modified:   services/jni/com_android_server_UsbHostManager.cpp

Change-Id: I386884715d1b732b06a63feb77790be6b59b6fe6
Signed-off-by: Robin Cutshaw <robin.cutshaw@gmail.com>
diff --git a/api/current.txt b/api/current.txt
index 6b893d5..4287988 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -10187,7 +10187,10 @@
     method public int getDeviceSubclass();
     method public android.hardware.usb.UsbInterface getInterface(int);
     method public int getInterfaceCount();
+    method public java.lang.String getManufacturerName();
     method public int getProductId();
+    method public java.lang.String getProductName();
+    method public java.lang.String getSerialNumber();
     method public int getVendorId();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator CREATOR;
diff --git a/core/java/android/hardware/usb/UsbDevice.java b/core/java/android/hardware/usb/UsbDevice.java
index 9bd38f9..d1e63f6 100644
--- a/core/java/android/hardware/usb/UsbDevice.java
+++ b/core/java/android/hardware/usb/UsbDevice.java
@@ -46,6 +46,9 @@
     private static final String TAG = "UsbDevice";
 
     private final String mName;
+    private final String mManufacturerName;
+    private final String mProductName;
+    private final String mSerialNumber;
     private final int mVendorId;
     private final int mProductId;
     private final int mClass;
@@ -58,13 +61,18 @@
      * @hide
      */
     public UsbDevice(String name, int vendorId, int productId,
-            int Class, int subClass, int protocol, Parcelable[] interfaces) {
+            int Class, int subClass, int protocol,
+            String manufacturerName, String productName, String serialNumber,
+            Parcelable[] interfaces) {
         mName = name;
         mVendorId = vendorId;
         mProductId = productId;
         mClass = Class;
         mSubclass = subClass;
         mProtocol = protocol;
+        mManufacturerName = manufacturerName;
+        mProductName = productName;
+        mSerialNumber = serialNumber;
         mInterfaces = interfaces;
     }
 
@@ -80,6 +88,33 @@
     }
 
     /**
+     * Returns the manufacturer name of the device.
+     *
+     * @return the manufacturer name
+     */
+    public String getManufacturerName() {
+        return mManufacturerName;
+    }
+
+    /**
+     * Returns the product name of the device.
+     *
+     * @return the product name
+     */
+    public String getProductName() {
+        return mProductName;
+    }
+
+    /**
+     * Returns the serial number of the device.
+     *
+     * @return the serial number name
+     */
+    public String getSerialNumber() {
+        return mSerialNumber;
+    }
+
+    /**
      * Returns a unique integer ID for the device.
      * This is a convenience for clients that want to use an integer to represent
      * the device, rather than the device name.
@@ -176,7 +211,8 @@
         return "UsbDevice[mName=" + mName + ",mVendorId=" + mVendorId +
                 ",mProductId=" + mProductId + ",mClass=" + mClass +
                 ",mSubclass=" + mSubclass + ",mProtocol=" + mProtocol +
-                ",mInterfaces=" + mInterfaces + "]";
+                ",mManufacturerName=" + mManufacturerName + ",mProductName=" + mProductName +
+                ",mSerialNumber=" + mSerialNumber + ",mInterfaces=" + mInterfaces + "]";
     }
 
     public static final Parcelable.Creator<UsbDevice> CREATOR =
@@ -188,8 +224,12 @@
             int clasz = in.readInt();
             int subClass = in.readInt();
             int protocol = in.readInt();
+            String manufacturerName = in.readString();
+            String productName = in.readString();
+            String serialNumber = in.readString();
             Parcelable[] interfaces = in.readParcelableArray(UsbInterface.class.getClassLoader());
-            return new UsbDevice(name, vendorId, productId, clasz, subClass, protocol, interfaces);
+            return new UsbDevice(name, vendorId, productId, clasz, subClass, protocol,
+                                 manufacturerName, productName, serialNumber, interfaces);
         }
 
         public UsbDevice[] newArray(int size) {
@@ -208,6 +248,9 @@
         parcel.writeInt(mClass);
         parcel.writeInt(mSubclass);
         parcel.writeInt(mProtocol);
+        parcel.writeString(mManufacturerName);
+        parcel.writeString(mProductName);
+        parcel.writeString(mSerialNumber);
         parcel.writeParcelableArray(mInterfaces, 0);
    }
 
diff --git a/services/java/com/android/server/usb/UsbHostManager.java b/services/java/com/android/server/usb/UsbHostManager.java
index 175ae6f..59a13ea 100644
--- a/services/java/com/android/server/usb/UsbHostManager.java
+++ b/services/java/com/android/server/usb/UsbHostManager.java
@@ -94,6 +94,7 @@
     /* Called from JNI in monitorUsbHostBus() to report new USB devices */
     private void usbDeviceAdded(String deviceName, int vendorID, int productID,
             int deviceClass, int deviceSubclass, int deviceProtocol,
+            String manufacturerName, String productName, String serialNumber,
             /* array of quintuples containing id, class, subclass, protocol
                and number of endpoints for each interface */
             int[] interfaceValues,
@@ -149,7 +150,8 @@
             }
 
             UsbDevice device = new UsbDevice(deviceName, vendorID, productID,
-                    deviceClass, deviceSubclass, deviceProtocol, interfaces);
+                    deviceClass, deviceSubclass, deviceProtocol,
+                    manufacturerName, productName, serialNumber, interfaces);
             mDevices.put(deviceName, device);
             getCurrentSettings().deviceAttached(device);
         }
diff --git a/services/java/com/android/server/usb/UsbSettingsManager.java b/services/java/com/android/server/usb/UsbSettingsManager.java
index 4b2bbfe..b2c2a3e 100644
--- a/services/java/com/android/server/usb/UsbSettingsManager.java
+++ b/services/java/com/android/server/usb/UsbSettingsManager.java
@@ -107,13 +107,23 @@
         public final int mSubclass;
         // USB device protocol (or -1 for unspecified)
         public final int mProtocol;
+        // USB device manufacturer name string (or null for unspecified)
+        public final String mManufacturerName;
+        // USB device product name string (or null for unspecified)
+        public final String mProductName;
+        // USB device serial number string (or null for unspecified)
+        public final String mSerialNumber;
 
-        public DeviceFilter(int vid, int pid, int clasz, int subclass, int protocol) {
+        public DeviceFilter(int vid, int pid, int clasz, int subclass, int protocol,
+                            String manufacturer, String product, String serialnum) {
             mVendorId = vid;
             mProductId = pid;
             mClass = clasz;
             mSubclass = subclass;
             mProtocol = protocol;
+            mManufacturerName = manufacturer;
+            mProductName = product;
+            mSerialNumber = serialnum;
         }
 
         public DeviceFilter(UsbDevice device) {
@@ -122,6 +132,9 @@
             mClass = device.getDeviceClass();
             mSubclass = device.getDeviceSubclass();
             mProtocol = device.getDeviceProtocol();
+            mManufacturerName = device.getManufacturerName();
+            mProductName = device.getProductName();
+            mSerialNumber = device.getSerialNumber();
         }
 
         public static DeviceFilter read(XmlPullParser parser)
@@ -131,27 +144,52 @@
             int deviceClass = -1;
             int deviceSubclass = -1;
             int deviceProtocol = -1;
+            String manufacturerName = null;
+            String productName = null;
+            String serialNumber = null;
 
             int count = parser.getAttributeCount();
             for (int i = 0; i < count; i++) {
                 String name = parser.getAttributeName(i);
-                // All attribute values are ints
-                int value = Integer.parseInt(parser.getAttributeValue(i));
-
-                if ("vendor-id".equals(name)) {
-                    vendorId = value;
-                } else if ("product-id".equals(name)) {
-                    productId = value;
-                } else if ("class".equals(name)) {
-                    deviceClass = value;
-                } else if ("subclass".equals(name)) {
-                    deviceSubclass = value;
-                } else if ("protocol".equals(name)) {
-                    deviceProtocol = value;
+                String value = parser.getAttributeValue(i);
+                // Attribute values are ints or strings
+                if ("manufacturer-name".equals(name)) {
+                    manufacturerName = value;
+                } else if ("product-name".equals(name)) {
+                    productName = value;
+                } else if ("serial-number".equals(name)) {
+                    serialNumber = value;
+                } else {
+                    int intValue = -1;
+                    int radix = 10;
+                    if (value != null && value.length() > 2 && value.charAt(0) == '0' &&
+                        (value.charAt(1) == 'x' || value.charAt(1) == 'X')) {
+                        // allow hex values starting with 0x or 0X
+                        radix = 16;
+                        value = value.substring(2);
+                    }
+                    try {
+                        intValue = Integer.parseInt(value, radix);
+                    } catch (NumberFormatException e) {
+                        Slog.e(TAG, "invalid number for field " + name, e);
+                        continue;
+                    }
+                    if ("vendor-id".equals(name)) {
+                        vendorId = intValue;
+                    } else if ("product-id".equals(name)) {
+                        productId = intValue;
+                    } else if ("class".equals(name)) {
+                        deviceClass = intValue;
+                    } else if ("subclass".equals(name)) {
+                        deviceSubclass = intValue;
+                    } else if ("protocol".equals(name)) {
+                        deviceProtocol = intValue;
+                    }
                 }
             }
             return new DeviceFilter(vendorId, productId,
-                    deviceClass, deviceSubclass, deviceProtocol);
+                    deviceClass, deviceSubclass, deviceProtocol,
+                    manufacturerName, productName, serialNumber);
         }
 
         public void write(XmlSerializer serializer) throws IOException {
@@ -171,6 +209,15 @@
             if (mProtocol != -1) {
                 serializer.attribute(null, "protocol", Integer.toString(mProtocol));
             }
+            if (mManufacturerName != null) {
+                serializer.attribute(null, "manufacturer-name", mManufacturerName);
+            }
+            if (mProductName != null) {
+                serializer.attribute(null, "product-name", mProductName);
+            }
+            if (mSerialNumber != null) {
+                serializer.attribute(null, "serial-number", mSerialNumber);
+            }
             serializer.endTag(null, "usb-device");
         }
 
@@ -183,6 +230,15 @@
         public boolean matches(UsbDevice device) {
             if (mVendorId != -1 && device.getVendorId() != mVendorId) return false;
             if (mProductId != -1 && device.getProductId() != mProductId) return false;
+            if (mManufacturerName != null && device.getManufacturerName() == null) return false;
+            if (mProductName != null && device.getProductName() == null) return false;
+            if (mSerialNumber != null && device.getSerialNumber() == null) return false;
+            if (mManufacturerName != null && device.getManufacturerName() != null &&
+                !mManufacturerName.equals(device.getManufacturerName())) return false;
+            if (mProductName != null && device.getProductName() != null &&
+                !mProductName.equals(device.getProductName())) return false;
+            if (mSerialNumber != null && device.getSerialNumber() != null &&
+                !mSerialNumber.equals(device.getSerialNumber())) return false;
 
             // check device class/subclass/protocol
             if (matches(device.getDeviceClass(), device.getDeviceSubclass(),
@@ -202,6 +258,15 @@
         public boolean matches(DeviceFilter f) {
             if (mVendorId != -1 && f.mVendorId != mVendorId) return false;
             if (mProductId != -1 && f.mProductId != mProductId) return false;
+            if (f.mManufacturerName != null && mManufacturerName == null) return false;
+            if (f.mProductName != null && mProductName == null) return false;
+            if (f.mSerialNumber != null && mSerialNumber == null) return false;
+            if (mManufacturerName != null && f.mManufacturerName != null &&
+                !mManufacturerName.equals(f.mManufacturerName)) return false;
+            if (mProductName != null && f.mProductName != null &&
+                !mProductName.equals(f.mProductName)) return false;
+            if (mSerialNumber != null && f.mSerialNumber != null &&
+                !mSerialNumber.equals(f.mSerialNumber)) return false;
 
             // check device class/subclass/protocol
             return matches(f.mClass, f.mSubclass, f.mProtocol);
@@ -216,19 +281,67 @@
             }
             if (obj instanceof DeviceFilter) {
                 DeviceFilter filter = (DeviceFilter)obj;
-                return (filter.mVendorId == mVendorId &&
-                        filter.mProductId == mProductId &&
-                        filter.mClass == mClass &&
-                        filter.mSubclass == mSubclass &&
-                        filter.mProtocol == mProtocol);
+
+                if (filter.mVendorId != mVendorId ||
+                        filter.mProductId != mProductId ||
+                        filter.mClass != mClass ||
+                        filter.mSubclass != mSubclass ||
+                        filter.mProtocol != mProtocol) {
+                    return(false);
+                }
+                if ((filter.mManufacturerName != null &&
+                        mManufacturerName == null) ||
+                    (filter.mManufacturerName == null &&
+                        mManufacturerName != null) ||
+                    (filter.mProductName != null &&
+                        mProductName == null)  ||
+                    (filter.mProductName == null &&
+                        mProductName != null) ||
+                    (filter.mSerialNumber != null &&
+                        mSerialNumber == null)  ||
+                    (filter.mSerialNumber == null &&
+                        mSerialNumber != null)) {
+                    return(false);
+                }
+                if  ((filter.mManufacturerName != null &&
+                        mManufacturerName != null &&
+                        !mManufacturerName.equals(filter.mManufacturerName)) ||
+                     (filter.mProductName != null &&
+                        mProductName != null &&
+                        !mProductName.equals(filter.mProductName)) ||
+                     (filter.mSerialNumber != null &&
+                        mSerialNumber != null &&
+                        !mSerialNumber.equals(filter.mSerialNumber))) {
+                    return(false);
+                }
+                return(true);
             }
             if (obj instanceof UsbDevice) {
                 UsbDevice device = (UsbDevice)obj;
-                return (device.getVendorId() == mVendorId &&
-                        device.getProductId() == mProductId &&
-                        device.getDeviceClass() == mClass &&
-                        device.getDeviceSubclass() == mSubclass &&
-                        device.getDeviceProtocol() == mProtocol);
+                if (device.getVendorId() != mVendorId ||
+                        device.getProductId() != mProductId ||
+                        device.getDeviceClass() != mClass ||
+                        device.getDeviceSubclass() != mSubclass ||
+                        device.getDeviceProtocol() != mProtocol) {
+                    return(false);
+                }
+                if ((mManufacturerName != null && device.getManufacturerName() == null) ||
+                        (mManufacturerName == null && device.getManufacturerName() != null) ||
+                        (mProductName != null && device.getProductName() == null) ||
+                        (mProductName == null && device.getProductName() != null) ||
+                        (mSerialNumber != null && device.getSerialNumber() == null) ||
+                        (mSerialNumber == null && device.getSerialNumber() != null)) {
+                    return(false);
+                }
+                if ((device.getManufacturerName() != null &&
+                        !mManufacturerName.equals(device.getManufacturerName())) ||
+                        (device.getProductName() != null &&
+                            !mProductName.equals(device.getProductName())) ||
+                        (device.getSerialNumber() != null &&
+                            !mSerialNumber.equals(device.getSerialNumber()))) {
+                    return(false);
+                }
+                return true;
             }
             return false;
         }
@@ -243,7 +356,9 @@
         public String toString() {
             return "DeviceFilter[mVendorId=" + mVendorId + ",mProductId=" + mProductId +
                     ",mClass=" + mClass + ",mSubclass=" + mSubclass +
-                    ",mProtocol=" + mProtocol + "]";
+                    ",mProtocol=" + mProtocol + ",mManufacturerName=" + mManufacturerName +
+                    ",mProductName=" + mProductName + ",mSerialNumber=" + mSerialNumber +
+                    "]";
         }
     }
 
diff --git a/services/jni/com_android_server_UsbHostManager.cpp b/services/jni/com_android_server_UsbHostManager.cpp
index d0a6cdf..b252313 100644
--- a/services/jni/com_android_server_UsbHostManager.cpp
+++ b/services/jni/com_android_server_UsbHostManager.cpp
@@ -72,6 +72,9 @@
     uint8_t deviceClass = deviceDesc->bDeviceClass;
     uint8_t deviceSubClass = deviceDesc->bDeviceSubClass;
     uint8_t protocol = deviceDesc->bDeviceProtocol;
+    char *manufacturer = usb_device_get_manufacturer_name(device);
+    char *product = usb_device_get_product_name(device);
+    char *serial = usb_device_get_serial(device);
 
     usb_descriptor_iter_init(device, &iter);
 
@@ -108,12 +111,19 @@
     env->SetIntArrayRegion(endpointArray, 0, length, endpointValues.array());
 
     jstring deviceName = env->NewStringUTF(devname);
+    jstring manufacturerName = env->NewStringUTF(manufacturer);
+    jstring productName = env->NewStringUTF(product);
+    jstring serialNumber = env->NewStringUTF(serial);
     env->CallVoidMethod(thiz, method_usbDeviceAdded,
             deviceName, vendorId, productId, deviceClass,
-            deviceSubClass, protocol, interfaceArray, endpointArray);
+            deviceSubClass, protocol, manufacturerName,
+            productName, serialNumber, interfaceArray, endpointArray);
 
     env->DeleteLocalRef(interfaceArray);
     env->DeleteLocalRef(endpointArray);
+    env->DeleteLocalRef(serialNumber);
+    env->DeleteLocalRef(productName);
+    env->DeleteLocalRef(manufacturerName);
     env->DeleteLocalRef(deviceName);
     checkAndClearExceptionFromCallback(env, __FUNCTION__);
 
@@ -178,7 +188,7 @@
         ALOGE("Can't find com/android/server/usb/UsbHostManager");
         return -1;
     }
-    method_usbDeviceAdded = env->GetMethodID(clazz, "usbDeviceAdded", "(Ljava/lang/String;IIIII[I[I)V");
+    method_usbDeviceAdded = env->GetMethodID(clazz, "usbDeviceAdded", "(Ljava/lang/String;IIIIILjava/lang/String;Ljava/lang/String;Ljava/lang/String;[I[I)V");
     if (method_usbDeviceAdded == NULL) {
         ALOGE("Can't find usbDeviceAdded");
         return -1;