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