Home | History | Annotate | Download | only in usb
      1 /*
      2  * Copyright 2017 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.hardware.usb;
     18 
     19 import android.annotation.NonNull;
     20 import android.service.usb.UsbDeviceFilterProto;
     21 import android.util.Slog;
     22 
     23 import com.android.internal.util.dump.DualDumpOutputStream;
     24 
     25 import org.xmlpull.v1.XmlPullParser;
     26 import org.xmlpull.v1.XmlPullParserException;
     27 import org.xmlpull.v1.XmlSerializer;
     28 
     29 import java.io.IOException;
     30 import java.util.Objects;
     31 
     32 /**
     33  * This class is used to describe a USB device.
     34  * When used in HashMaps all values must be specified,
     35  * but wildcards can be used for any of the fields in
     36  * the package meta-data.
     37  *
     38  * @hide
     39  */
     40 public class DeviceFilter {
     41     private static final String TAG = DeviceFilter.class.getSimpleName();
     42 
     43     // USB Vendor ID (or -1 for unspecified)
     44     public final int mVendorId;
     45     // USB Product ID (or -1 for unspecified)
     46     public final int mProductId;
     47     // USB device or interface class (or -1 for unspecified)
     48     public final int mClass;
     49     // USB device subclass (or -1 for unspecified)
     50     public final int mSubclass;
     51     // USB device protocol (or -1 for unspecified)
     52     public final int mProtocol;
     53     // USB device manufacturer name string (or null for unspecified)
     54     public final String mManufacturerName;
     55     // USB device product name string (or null for unspecified)
     56     public final String mProductName;
     57     // USB device serial number string (or null for unspecified)
     58     public final String mSerialNumber;
     59 
     60     public DeviceFilter(int vid, int pid, int clasz, int subclass, int protocol,
     61             String manufacturer, String product, String serialnum) {
     62         mVendorId = vid;
     63         mProductId = pid;
     64         mClass = clasz;
     65         mSubclass = subclass;
     66         mProtocol = protocol;
     67         mManufacturerName = manufacturer;
     68         mProductName = product;
     69         mSerialNumber = serialnum;
     70     }
     71 
     72     public DeviceFilter(UsbDevice device) {
     73         mVendorId = device.getVendorId();
     74         mProductId = device.getProductId();
     75         mClass = device.getDeviceClass();
     76         mSubclass = device.getDeviceSubclass();
     77         mProtocol = device.getDeviceProtocol();
     78         mManufacturerName = device.getManufacturerName();
     79         mProductName = device.getProductName();
     80         mSerialNumber = device.getSerialNumber();
     81     }
     82 
     83     public static DeviceFilter read(XmlPullParser parser)
     84             throws XmlPullParserException, IOException {
     85         int vendorId = -1;
     86         int productId = -1;
     87         int deviceClass = -1;
     88         int deviceSubclass = -1;
     89         int deviceProtocol = -1;
     90         String manufacturerName = null;
     91         String productName = null;
     92         String serialNumber = null;
     93 
     94         int count = parser.getAttributeCount();
     95         for (int i = 0; i < count; i++) {
     96             String name = parser.getAttributeName(i);
     97             String value = parser.getAttributeValue(i);
     98             // Attribute values are ints or strings
     99             if ("manufacturer-name".equals(name)) {
    100                 manufacturerName = value;
    101             } else if ("product-name".equals(name)) {
    102                 productName = value;
    103             } else if ("serial-number".equals(name)) {
    104                 serialNumber = value;
    105             } else {
    106                 int intValue;
    107                 int radix = 10;
    108                 if (value != null && value.length() > 2 && value.charAt(0) == '0' &&
    109                         (value.charAt(1) == 'x' || value.charAt(1) == 'X')) {
    110                     // allow hex values starting with 0x or 0X
    111                     radix = 16;
    112                     value = value.substring(2);
    113                 }
    114                 try {
    115                     intValue = Integer.parseInt(value, radix);
    116                 } catch (NumberFormatException e) {
    117                     Slog.e(TAG, "invalid number for field " + name, e);
    118                     continue;
    119                 }
    120                 if ("vendor-id".equals(name)) {
    121                     vendorId = intValue;
    122                 } else if ("product-id".equals(name)) {
    123                     productId = intValue;
    124                 } else if ("class".equals(name)) {
    125                     deviceClass = intValue;
    126                 } else if ("subclass".equals(name)) {
    127                     deviceSubclass = intValue;
    128                 } else if ("protocol".equals(name)) {
    129                     deviceProtocol = intValue;
    130                 }
    131             }
    132         }
    133         return new DeviceFilter(vendorId, productId,
    134                 deviceClass, deviceSubclass, deviceProtocol,
    135                 manufacturerName, productName, serialNumber);
    136     }
    137 
    138     public void write(XmlSerializer serializer) throws IOException {
    139         serializer.startTag(null, "usb-device");
    140         if (mVendorId != -1) {
    141             serializer.attribute(null, "vendor-id", Integer.toString(mVendorId));
    142         }
    143         if (mProductId != -1) {
    144             serializer.attribute(null, "product-id", Integer.toString(mProductId));
    145         }
    146         if (mClass != -1) {
    147             serializer.attribute(null, "class", Integer.toString(mClass));
    148         }
    149         if (mSubclass != -1) {
    150             serializer.attribute(null, "subclass", Integer.toString(mSubclass));
    151         }
    152         if (mProtocol != -1) {
    153             serializer.attribute(null, "protocol", Integer.toString(mProtocol));
    154         }
    155         if (mManufacturerName != null) {
    156             serializer.attribute(null, "manufacturer-name", mManufacturerName);
    157         }
    158         if (mProductName != null) {
    159             serializer.attribute(null, "product-name", mProductName);
    160         }
    161         if (mSerialNumber != null) {
    162             serializer.attribute(null, "serial-number", mSerialNumber);
    163         }
    164         serializer.endTag(null, "usb-device");
    165     }
    166 
    167     private boolean matches(int clasz, int subclass, int protocol) {
    168         return ((mClass == -1 || clasz == mClass) &&
    169                 (mSubclass == -1 || subclass == mSubclass) &&
    170                 (mProtocol == -1 || protocol == mProtocol));
    171     }
    172 
    173     public boolean matches(UsbDevice device) {
    174         if (mVendorId != -1 && device.getVendorId() != mVendorId) return false;
    175         if (mProductId != -1 && device.getProductId() != mProductId) return false;
    176         if (mManufacturerName != null && device.getManufacturerName() == null) return false;
    177         if (mProductName != null && device.getProductName() == null) return false;
    178         if (mSerialNumber != null && device.getSerialNumber() == null) return false;
    179         if (mManufacturerName != null && device.getManufacturerName() != null &&
    180                 !mManufacturerName.equals(device.getManufacturerName())) return false;
    181         if (mProductName != null && device.getProductName() != null &&
    182                 !mProductName.equals(device.getProductName())) return false;
    183         if (mSerialNumber != null && device.getSerialNumber() != null &&
    184                 !mSerialNumber.equals(device.getSerialNumber())) return false;
    185 
    186         // check device class/subclass/protocol
    187         if (matches(device.getDeviceClass(), device.getDeviceSubclass(),
    188                 device.getDeviceProtocol())) return true;
    189 
    190         // if device doesn't match, check the interfaces
    191         int count = device.getInterfaceCount();
    192         for (int i = 0; i < count; i++) {
    193             UsbInterface intf = device.getInterface(i);
    194             if (matches(intf.getInterfaceClass(), intf.getInterfaceSubclass(),
    195                     intf.getInterfaceProtocol())) return true;
    196         }
    197 
    198         return false;
    199     }
    200 
    201     /**
    202      * If the device described by {@code device} covered by this filter?
    203      *
    204      * @param device The device
    205      *
    206      * @return {@code true} iff this filter covers the {@code device}
    207      */
    208     public boolean contains(DeviceFilter device) {
    209         // -1 and null means "match anything"
    210 
    211         if (mVendorId != -1 && device.mVendorId != mVendorId) return false;
    212         if (mProductId != -1 && device.mProductId != mProductId) return false;
    213         if (mManufacturerName != null && !Objects.equals(mManufacturerName,
    214                 device.mManufacturerName)) {
    215             return false;
    216         }
    217         if (mProductName != null && !Objects.equals(mProductName, device.mProductName)) {
    218             return false;
    219         }
    220         if (mSerialNumber != null
    221                 && !Objects.equals(mSerialNumber, device.mSerialNumber)) {
    222             return false;
    223         }
    224 
    225         // check device class/subclass/protocol
    226         return matches(device.mClass, device.mSubclass, device.mProtocol);
    227     }
    228 
    229     @Override
    230     public boolean equals(Object obj) {
    231         // can't compare if we have wildcard strings
    232         if (mVendorId == -1 || mProductId == -1 ||
    233                 mClass == -1 || mSubclass == -1 || mProtocol == -1) {
    234             return false;
    235         }
    236         if (obj instanceof DeviceFilter) {
    237             DeviceFilter filter = (DeviceFilter)obj;
    238 
    239             if (filter.mVendorId != mVendorId ||
    240                     filter.mProductId != mProductId ||
    241                     filter.mClass != mClass ||
    242                     filter.mSubclass != mSubclass ||
    243                     filter.mProtocol != mProtocol) {
    244                 return(false);
    245             }
    246             if ((filter.mManufacturerName != null &&
    247                     mManufacturerName == null) ||
    248                     (filter.mManufacturerName == null &&
    249                             mManufacturerName != null) ||
    250                     (filter.mProductName != null &&
    251                             mProductName == null)  ||
    252                     (filter.mProductName == null &&
    253                             mProductName != null) ||
    254                     (filter.mSerialNumber != null &&
    255                             mSerialNumber == null)  ||
    256                     (filter.mSerialNumber == null &&
    257                             mSerialNumber != null)) {
    258                 return(false);
    259             }
    260             if  ((filter.mManufacturerName != null &&
    261                     mManufacturerName != null &&
    262                     !mManufacturerName.equals(filter.mManufacturerName)) ||
    263                     (filter.mProductName != null &&
    264                             mProductName != null &&
    265                             !mProductName.equals(filter.mProductName)) ||
    266                     (filter.mSerialNumber != null &&
    267                             mSerialNumber != null &&
    268                             !mSerialNumber.equals(filter.mSerialNumber))) {
    269                 return false;
    270             }
    271             return true;
    272         }
    273         if (obj instanceof UsbDevice) {
    274             UsbDevice device = (UsbDevice)obj;
    275             if (device.getVendorId() != mVendorId ||
    276                     device.getProductId() != mProductId ||
    277                     device.getDeviceClass() != mClass ||
    278                     device.getDeviceSubclass() != mSubclass ||
    279                     device.getDeviceProtocol() != mProtocol) {
    280                 return(false);
    281             }
    282             if ((mManufacturerName != null && device.getManufacturerName() == null) ||
    283                     (mManufacturerName == null && device.getManufacturerName() != null) ||
    284                     (mProductName != null && device.getProductName() == null) ||
    285                     (mProductName == null && device.getProductName() != null) ||
    286                     (mSerialNumber != null && device.getSerialNumber() == null) ||
    287                     (mSerialNumber == null && device.getSerialNumber() != null)) {
    288                 return(false);
    289             }
    290             if ((device.getManufacturerName() != null &&
    291                     !mManufacturerName.equals(device.getManufacturerName())) ||
    292                     (device.getProductName() != null &&
    293                             !mProductName.equals(device.getProductName())) ||
    294                     (device.getSerialNumber() != null &&
    295                             !mSerialNumber.equals(device.getSerialNumber()))) {
    296                 return false;
    297             }
    298             return true;
    299         }
    300         return false;
    301     }
    302 
    303     @Override
    304     public int hashCode() {
    305         return (((mVendorId << 16) | mProductId) ^
    306                 ((mClass << 16) | (mSubclass << 8) | mProtocol));
    307     }
    308 
    309     @Override
    310     public String toString() {
    311         return "DeviceFilter[mVendorId=" + mVendorId + ",mProductId=" + mProductId +
    312                 ",mClass=" + mClass + ",mSubclass=" + mSubclass +
    313                 ",mProtocol=" + mProtocol + ",mManufacturerName=" + mManufacturerName +
    314                 ",mProductName=" + mProductName + ",mSerialNumber=" + mSerialNumber +
    315                 "]";
    316     }
    317 
    318     /**
    319      * Write a description of the filter to a dump stream.
    320      */
    321     public void dump(@NonNull DualDumpOutputStream dump, String idName, long id) {
    322         long token = dump.start(idName, id);
    323 
    324         dump.write("vendor_id", UsbDeviceFilterProto.VENDOR_ID, mVendorId);
    325         dump.write("product_id", UsbDeviceFilterProto.PRODUCT_ID, mProductId);
    326         dump.write("class", UsbDeviceFilterProto.CLASS, mClass);
    327         dump.write("subclass", UsbDeviceFilterProto.SUBCLASS, mSubclass);
    328         dump.write("protocol", UsbDeviceFilterProto.PROTOCOL, mProtocol);
    329         dump.write("manufacturer_name", UsbDeviceFilterProto.MANUFACTURER_NAME, mManufacturerName);
    330         dump.write("product_name", UsbDeviceFilterProto.PRODUCT_NAME, mProductName);
    331         dump.write("serial_number", UsbDeviceFilterProto.SERIAL_NUMBER, mSerialNumber);
    332 
    333         dump.end(token);
    334     }
    335 }
    336