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