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