blob: da979c0c3fb17cedf577b7f98d28f35e3f1a7dee [file] [log] [blame]
Philip P. Moltmann5a633c62017-11-09 15:55:24 -08001/*
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
17package android.hardware.usb;
18
Philip P. Moltmann371a3b82018-01-26 13:00:22 -080019import android.annotation.NonNull;
20import android.service.usb.UsbDeviceFilterProto;
Philip P. Moltmann5a633c62017-11-09 15:55:24 -080021import android.util.Slog;
22
Philip P. Moltmann371a3b82018-01-26 13:00:22 -080023import com.android.internal.util.dump.DualDumpOutputStream;
24
Philip P. Moltmann5a633c62017-11-09 15:55:24 -080025import org.xmlpull.v1.XmlPullParser;
26import org.xmlpull.v1.XmlPullParserException;
27import org.xmlpull.v1.XmlSerializer;
28
29import java.io.IOException;
30import 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 */
40public 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
Evan Severson7b78bbd2019-08-29 16:50:27 -070083 public DeviceFilter(@NonNull DeviceFilter filter) {
84 mVendorId = filter.mVendorId;
85 mProductId = filter.mProductId;
86 mClass = filter.mClass;
87 mSubclass = filter.mSubclass;
88 mProtocol = filter.mProtocol;
89 mManufacturerName = filter.mManufacturerName;
90 mProductName = filter.mProductName;
91 mSerialNumber = filter.mSerialNumber;
92 }
93
Philip P. Moltmann5a633c62017-11-09 15:55:24 -080094 public static DeviceFilter read(XmlPullParser parser)
95 throws XmlPullParserException, IOException {
96 int vendorId = -1;
97 int productId = -1;
98 int deviceClass = -1;
99 int deviceSubclass = -1;
100 int deviceProtocol = -1;
101 String manufacturerName = null;
102 String productName = null;
103 String serialNumber = 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 ("manufacturer-name".equals(name)) {
111 manufacturerName = value;
112 } else if ("product-name".equals(name)) {
113 productName = value;
114 } else if ("serial-number".equals(name)) {
115 serialNumber = value;
116 } else {
117 int intValue;
118 int radix = 10;
119 if (value != null && value.length() > 2 && value.charAt(0) == '0' &&
120 (value.charAt(1) == 'x' || value.charAt(1) == 'X')) {
121 // allow hex values starting with 0x or 0X
122 radix = 16;
123 value = value.substring(2);
124 }
125 try {
126 intValue = Integer.parseInt(value, radix);
127 } catch (NumberFormatException e) {
128 Slog.e(TAG, "invalid number for field " + name, e);
129 continue;
130 }
131 if ("vendor-id".equals(name)) {
132 vendorId = intValue;
133 } else if ("product-id".equals(name)) {
134 productId = intValue;
135 } else if ("class".equals(name)) {
136 deviceClass = intValue;
137 } else if ("subclass".equals(name)) {
138 deviceSubclass = intValue;
139 } else if ("protocol".equals(name)) {
140 deviceProtocol = intValue;
141 }
142 }
143 }
144 return new DeviceFilter(vendorId, productId,
145 deviceClass, deviceSubclass, deviceProtocol,
146 manufacturerName, productName, serialNumber);
147 }
148
149 public void write(XmlSerializer serializer) throws IOException {
150 serializer.startTag(null, "usb-device");
151 if (mVendorId != -1) {
152 serializer.attribute(null, "vendor-id", Integer.toString(mVendorId));
153 }
154 if (mProductId != -1) {
155 serializer.attribute(null, "product-id", Integer.toString(mProductId));
156 }
157 if (mClass != -1) {
158 serializer.attribute(null, "class", Integer.toString(mClass));
159 }
160 if (mSubclass != -1) {
161 serializer.attribute(null, "subclass", Integer.toString(mSubclass));
162 }
163 if (mProtocol != -1) {
164 serializer.attribute(null, "protocol", Integer.toString(mProtocol));
165 }
166 if (mManufacturerName != null) {
167 serializer.attribute(null, "manufacturer-name", mManufacturerName);
168 }
169 if (mProductName != null) {
170 serializer.attribute(null, "product-name", mProductName);
171 }
172 if (mSerialNumber != null) {
173 serializer.attribute(null, "serial-number", mSerialNumber);
174 }
175 serializer.endTag(null, "usb-device");
176 }
177
178 private boolean matches(int clasz, int subclass, int protocol) {
179 return ((mClass == -1 || clasz == mClass) &&
180 (mSubclass == -1 || subclass == mSubclass) &&
181 (mProtocol == -1 || protocol == mProtocol));
182 }
183
184 public boolean matches(UsbDevice device) {
185 if (mVendorId != -1 && device.getVendorId() != mVendorId) return false;
186 if (mProductId != -1 && device.getProductId() != mProductId) return false;
187 if (mManufacturerName != null && device.getManufacturerName() == null) return false;
188 if (mProductName != null && device.getProductName() == null) return false;
189 if (mSerialNumber != null && device.getSerialNumber() == null) return false;
190 if (mManufacturerName != null && device.getManufacturerName() != null &&
191 !mManufacturerName.equals(device.getManufacturerName())) return false;
192 if (mProductName != null && device.getProductName() != null &&
193 !mProductName.equals(device.getProductName())) return false;
194 if (mSerialNumber != null && device.getSerialNumber() != null &&
195 !mSerialNumber.equals(device.getSerialNumber())) return false;
196
197 // check device class/subclass/protocol
198 if (matches(device.getDeviceClass(), device.getDeviceSubclass(),
199 device.getDeviceProtocol())) return true;
200
201 // if device doesn't match, check the interfaces
202 int count = device.getInterfaceCount();
203 for (int i = 0; i < count; i++) {
204 UsbInterface intf = device.getInterface(i);
205 if (matches(intf.getInterfaceClass(), intf.getInterfaceSubclass(),
206 intf.getInterfaceProtocol())) return true;
207 }
208
209 return false;
210 }
211
212 /**
213 * If the device described by {@code device} covered by this filter?
214 *
215 * @param device The device
216 *
217 * @return {@code true} iff this filter covers the {@code device}
218 */
219 public boolean contains(DeviceFilter device) {
220 // -1 and null means "match anything"
221
222 if (mVendorId != -1 && device.mVendorId != mVendorId) return false;
223 if (mProductId != -1 && device.mProductId != mProductId) return false;
224 if (mManufacturerName != null && !Objects.equals(mManufacturerName,
225 device.mManufacturerName)) {
226 return false;
227 }
228 if (mProductName != null && !Objects.equals(mProductName, device.mProductName)) {
229 return false;
230 }
231 if (mSerialNumber != null
232 && !Objects.equals(mSerialNumber, device.mSerialNumber)) {
233 return false;
234 }
235
236 // check device class/subclass/protocol
237 return matches(device.mClass, device.mSubclass, device.mProtocol);
238 }
239
240 @Override
241 public boolean equals(Object obj) {
242 // can't compare if we have wildcard strings
243 if (mVendorId == -1 || mProductId == -1 ||
244 mClass == -1 || mSubclass == -1 || mProtocol == -1) {
245 return false;
246 }
247 if (obj instanceof DeviceFilter) {
248 DeviceFilter filter = (DeviceFilter)obj;
249
250 if (filter.mVendorId != mVendorId ||
251 filter.mProductId != mProductId ||
252 filter.mClass != mClass ||
253 filter.mSubclass != mSubclass ||
254 filter.mProtocol != mProtocol) {
255 return(false);
256 }
257 if ((filter.mManufacturerName != null &&
258 mManufacturerName == null) ||
259 (filter.mManufacturerName == null &&
260 mManufacturerName != null) ||
261 (filter.mProductName != null &&
262 mProductName == null) ||
263 (filter.mProductName == null &&
264 mProductName != null) ||
265 (filter.mSerialNumber != null &&
266 mSerialNumber == null) ||
267 (filter.mSerialNumber == null &&
268 mSerialNumber != null)) {
269 return(false);
270 }
271 if ((filter.mManufacturerName != null &&
272 mManufacturerName != null &&
273 !mManufacturerName.equals(filter.mManufacturerName)) ||
274 (filter.mProductName != null &&
275 mProductName != null &&
276 !mProductName.equals(filter.mProductName)) ||
277 (filter.mSerialNumber != null &&
278 mSerialNumber != null &&
279 !mSerialNumber.equals(filter.mSerialNumber))) {
280 return false;
281 }
282 return true;
283 }
284 if (obj instanceof UsbDevice) {
285 UsbDevice device = (UsbDevice)obj;
286 if (device.getVendorId() != mVendorId ||
287 device.getProductId() != mProductId ||
288 device.getDeviceClass() != mClass ||
289 device.getDeviceSubclass() != mSubclass ||
290 device.getDeviceProtocol() != mProtocol) {
291 return(false);
292 }
293 if ((mManufacturerName != null && device.getManufacturerName() == null) ||
294 (mManufacturerName == null && device.getManufacturerName() != null) ||
295 (mProductName != null && device.getProductName() == null) ||
296 (mProductName == null && device.getProductName() != null) ||
297 (mSerialNumber != null && device.getSerialNumber() == null) ||
298 (mSerialNumber == null && device.getSerialNumber() != null)) {
299 return(false);
300 }
301 if ((device.getManufacturerName() != null &&
302 !mManufacturerName.equals(device.getManufacturerName())) ||
303 (device.getProductName() != null &&
304 !mProductName.equals(device.getProductName())) ||
305 (device.getSerialNumber() != null &&
306 !mSerialNumber.equals(device.getSerialNumber()))) {
307 return false;
308 }
309 return true;
310 }
311 return false;
312 }
313
314 @Override
315 public int hashCode() {
316 return (((mVendorId << 16) | mProductId) ^
317 ((mClass << 16) | (mSubclass << 8) | mProtocol));
318 }
319
320 @Override
321 public String toString() {
322 return "DeviceFilter[mVendorId=" + mVendorId + ",mProductId=" + mProductId +
323 ",mClass=" + mClass + ",mSubclass=" + mSubclass +
324 ",mProtocol=" + mProtocol + ",mManufacturerName=" + mManufacturerName +
325 ",mProductName=" + mProductName + ",mSerialNumber=" + mSerialNumber +
326 "]";
327 }
Philip P. Moltmann371a3b82018-01-26 13:00:22 -0800328
329 /**
330 * Write a description of the filter to a dump stream.
331 */
332 public void dump(@NonNull DualDumpOutputStream dump, String idName, long id) {
333 long token = dump.start(idName, id);
334
335 dump.write("vendor_id", UsbDeviceFilterProto.VENDOR_ID, mVendorId);
336 dump.write("product_id", UsbDeviceFilterProto.PRODUCT_ID, mProductId);
337 dump.write("class", UsbDeviceFilterProto.CLASS, mClass);
338 dump.write("subclass", UsbDeviceFilterProto.SUBCLASS, mSubclass);
339 dump.write("protocol", UsbDeviceFilterProto.PROTOCOL, mProtocol);
340 dump.write("manufacturer_name", UsbDeviceFilterProto.MANUFACTURER_NAME, mManufacturerName);
341 dump.write("product_name", UsbDeviceFilterProto.PRODUCT_NAME, mProductName);
342 dump.write("serial_number", UsbDeviceFilterProto.SERIAL_NUMBER, mSerialNumber);
343
344 dump.end(token);
345 }
Philip P. Moltmann5a633c62017-11-09 15:55:24 -0800346}