1 /*
2  * Copyright (C) 2019 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.car.usb.handler;
18 
19 import android.hardware.usb.UsbDevice;
20 import android.hardware.usb.UsbInterface;
21 import android.util.Log;
22 
23 import org.xmlpull.v1.XmlPullParser;
24 
25 /**
26  * This class is used to describe a USB device. When used in HashMaps all values must be specified,
27  *  but wildcards can be used for any of the fields in the package meta-data.
28  */
29 class UsbDeviceFilter {
30     private static final String TAG = UsbDeviceFilter.class.getSimpleName();
31 
32     // USB Vendor ID (or -1 for unspecified)
33     public final int mVendorId;
34     // USB Product ID (or -1 for unspecified)
35     public final int mProductId;
36     // USB device or interface class (or -1 for unspecified)
37     public final int mClass;
38     // USB device subclass (or -1 for unspecified)
39     public final int mSubclass;
40     // USB device protocol (or -1 for unspecified)
41     public final int mProtocol;
42     // USB device manufacturer name string (or null for unspecified)
43     public final String mManufacturerName;
44     // USB device product name string (or null for unspecified)
45     public final String mProductName;
46     // USB device serial number string (or null for unspecified)
47     public final String mSerialNumber;
48 
49     // USB device in AOAP mode manufacturer
50     public final String mAoapManufacturer;
51     // USB device in AOAP mode model
52     public final String mAoapModel;
53     // USB device in AOAP mode description string
54     public final String mAoapDescription;
55     // USB device in AOAP mode version
56     public final String mAoapVersion;
57     // USB device in AOAP mode URI
58     public final String mAoapUri;
59     // USB device in AOAP mode serial
60     public final String mAoapSerial;
61     // USB device in AOAP mode verification service
62     public final String mAoapService;
63 
UsbDeviceFilter(int vid, int pid, int clasz, int subclass, int protocol, String manufacturer, String product, String serialnum, String aoapManufacturer, String aoapModel, String aoapDescription, String aoapVersion, String aoapUri, String aoapSerial, String aoapService)64     UsbDeviceFilter(int vid, int pid, int clasz, int subclass, int protocol,
65                         String manufacturer, String product, String serialnum,
66                         String aoapManufacturer, String aoapModel, String aoapDescription,
67                         String aoapVersion, String aoapUri, String aoapSerial,
68                         String aoapService) {
69         mVendorId = vid;
70         mProductId = pid;
71         mClass = clasz;
72         mSubclass = subclass;
73         mProtocol = protocol;
74         mManufacturerName = manufacturer;
75         mProductName = product;
76         mSerialNumber = serialnum;
77 
78         mAoapManufacturer = aoapManufacturer;
79         mAoapModel = aoapModel;
80         mAoapDescription = aoapDescription;
81         mAoapVersion = aoapVersion;
82         mAoapUri = aoapUri;
83         mAoapSerial = aoapSerial;
84         mAoapService = aoapService;
85     }
86 
read(XmlPullParser parser, boolean aoapData)87     public static UsbDeviceFilter read(XmlPullParser parser, boolean aoapData) {
88         int vendorId = -1;
89         int productId = -1;
90         int deviceClass = -1;
91         int deviceSubclass = -1;
92         int deviceProtocol = -1;
93         String manufacturerName = null;
94         String productName = null;
95         String serialNumber = null;
96 
97         String aoapManufacturer = null;
98         String aoapModel = null;
99         String aoapDescription = null;
100         String aoapVersion = null;
101         String aoapUri = null;
102         String aoapSerial = null;
103         String aoapService = null;
104 
105         int count = parser.getAttributeCount();
106         for (int i = 0; i < count; i++) {
107             String name = parser.getAttributeName(i);
108             String value = parser.getAttributeValue(i);
109             // Attribute values are ints or strings
110             if (!aoapData && "manufacturer-name".equals(name)) {
111                 manufacturerName = value;
112             } else if (!aoapData && "product-name".equals(name)) {
113                 productName = value;
114             } else if (!aoapData && "serial-number".equals(name)) {
115                 serialNumber = value;
116             } else if (aoapData && "manufacturer".equals(name)) {
117                 aoapManufacturer = value;
118             } else if (aoapData && "model".equals(name)) {
119                 aoapModel = value;
120             } else if (aoapData && "description".equals(name)) {
121                 aoapDescription = value;
122             } else if (aoapData && "version".equals(name)) {
123                 aoapVersion = value;
124             } else if (aoapData && "uri".equals(name)) {
125                 aoapUri = value;
126             } else if (aoapData && "serial".equals(name)) {
127                 aoapSerial = value;
128             } else if (aoapData && "service".equals(name)) {
129                 aoapService = value;
130             } else if (!aoapData) {
131                 int intValue = -1;
132                 int radix = 10;
133                 if (value != null && value.length() > 2 && value.charAt(0) == '0'
134                         && (value.charAt(1) == 'x' || value.charAt(1) == 'X')) {
135                     // allow hex values starting with 0x or 0X
136                     radix = 16;
137                     value = value.substring(2);
138                 }
139                 try {
140                     intValue = Integer.parseInt(value, radix);
141                 } catch (NumberFormatException e) {
142                     Log.e(TAG, "invalid number for field " + name, e);
143                     continue;
144                 }
145                 if ("vendor-id".equals(name)) {
146                     vendorId = intValue;
147                 } else if ("product-id".equals(name)) {
148                     productId = intValue;
149                 } else if ("class".equals(name)) {
150                     deviceClass = intValue;
151                 } else if ("subclass".equals(name)) {
152                     deviceSubclass = intValue;
153                 } else if ("protocol".equals(name)) {
154                     deviceProtocol = intValue;
155                 }
156             }
157         }
158         return new UsbDeviceFilter(vendorId, productId,
159                                 deviceClass, deviceSubclass, deviceProtocol,
160                                 manufacturerName, productName, serialNumber, aoapManufacturer,
161                                 aoapModel, aoapDescription, aoapVersion, aoapUri, aoapSerial,
162                                 aoapService);
163     }
164 
matches(int clasz, int subclass, int protocol)165     private boolean matches(int clasz, int subclass, int protocol) {
166         return ((mClass == -1 || clasz == mClass)
167                 && (mSubclass == -1 || subclass == mSubclass)
168                 && (mProtocol == -1 || protocol == mProtocol));
169     }
170 
matches(UsbDevice device)171     public boolean matches(UsbDevice device) {
172         if (mVendorId != -1 && device.getVendorId() != mVendorId) {
173             return false;
174         }
175         if (mProductId != -1 && device.getProductId() != mProductId) {
176             return false;
177         }
178         if (mManufacturerName != null && device.getManufacturerName() == null) {
179             return false;
180         }
181         if (mProductName != null && device.getProductName() == null) {
182             return false;
183         }
184         if (mSerialNumber != null && device.getSerialNumber() == null) {
185             return false;
186         }
187         if (mManufacturerName != null && device.getManufacturerName() != null
188                 && !mManufacturerName.equals(device.getManufacturerName())) {
189             return false;
190         }
191         if (mProductName != null && device.getProductName() != null
192                 && !mProductName.equals(device.getProductName())) {
193             return false;
194         }
195         if (mSerialNumber != null && device.getSerialNumber() != null
196                 && !mSerialNumber.equals(device.getSerialNumber())) {
197             return false;
198         }
199 
200         // check device class/subclass/protocol
201         if (matches(device.getDeviceClass(), device.getDeviceSubclass(),
202                     device.getDeviceProtocol())) {
203             return true;
204         }
205 
206         // if device doesn't match, check the interfaces
207         int count = device.getInterfaceCount();
208         for (int i = 0; i < count; i++) {
209             UsbInterface intf = device.getInterface(i);
210             if (matches(intf.getInterfaceClass(), intf.getInterfaceSubclass(),
211                         intf.getInterfaceProtocol())) {
212                 return true;
213             }
214         }
215 
216         return false;
217     }
218 
219     @Override
equals(Object obj)220     public boolean equals(Object obj) {
221         // can't compare if we have wildcard strings
222         if (mVendorId == -1 || mProductId == -1
223                 || mClass == -1 || mSubclass == -1 || mProtocol == -1) {
224             return false;
225         }
226         if (obj instanceof UsbDeviceFilter) {
227             UsbDeviceFilter filter = (UsbDeviceFilter) obj;
228 
229             if (filter.mVendorId != mVendorId
230                     || filter.mProductId != mProductId
231                     || filter.mClass != mClass
232                     || filter.mSubclass != mSubclass
233                     || filter.mProtocol != mProtocol) {
234                 return false;
235             }
236             if ((filter.mManufacturerName != null && mManufacturerName == null)
237                     || (filter.mManufacturerName == null && mManufacturerName != null)
238                     || (filter.mProductName != null && mProductName == null)
239                     || (filter.mProductName == null && mProductName != null)
240                     || (filter.mSerialNumber != null && mSerialNumber == null)
241                     || (filter.mSerialNumber == null && mSerialNumber != null)) {
242                 return false;
243             }
244             if  ((filter.mManufacturerName != null && mManufacturerName != null
245                       && !mManufacturerName.equals(filter.mManufacturerName))
246                       || (filter.mProductName != null && mProductName != null
247                       && !mProductName.equals(filter.mProductName))
248                       || (filter.mSerialNumber != null && mSerialNumber != null
249                       && !mSerialNumber.equals(filter.mSerialNumber))) {
250                 return false;
251             }
252             return true;
253         }
254         if (obj instanceof UsbDevice) {
255             UsbDevice device = (UsbDevice) obj;
256             if (device.getVendorId() != mVendorId
257                     || device.getProductId() != mProductId
258                     || device.getDeviceClass() != mClass
259                     || device.getDeviceSubclass() != mSubclass
260                     || device.getDeviceProtocol() != mProtocol) {
261                 return false;
262             }
263             if ((mManufacturerName != null && device.getManufacturerName() == null)
264                     || (mManufacturerName == null && device.getManufacturerName() != null)
265                     || (mProductName != null && device.getProductName() == null)
266                     || (mProductName == null && device.getProductName() != null)
267                     || (mSerialNumber != null && device.getSerialNumber() == null)
268                     || (mSerialNumber == null && device.getSerialNumber() != null)) {
269                 return false;
270             }
271             if ((device.getManufacturerName() != null
272                     && !mManufacturerName.equals(device.getManufacturerName()))
273                     || (device.getProductName() != null
274                     && !mProductName.equals(device.getProductName()))
275                     || (device.getSerialNumber() != null
276                     && !mSerialNumber.equals(device.getSerialNumber()))) {
277                 return false;
278             }
279             return true;
280         }
281         return false;
282     }
283 
284     @Override
hashCode()285     public int hashCode() {
286         return (((mVendorId << 16) | mProductId)
287                 ^ ((mClass << 16) | (mSubclass << 8) | mProtocol));
288     }
289 
290     @Override
toString()291     public String toString() {
292         return "DeviceFilter[mVendorId=" + mVendorId + ",mProductId=" + mProductId
293                 + ",mClass=" + mClass + ",mSubclass=" + mSubclass
294                 + ",mProtocol=" + mProtocol + ",mManufacturerName=" + mManufacturerName
295                 + ",mProductName=" + mProductName + ",mSerialNumber=" + mSerialNumber + "]";
296     }
297 }
298