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