1 /*
2  * Copyright (C) 2011 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 an
14  * limitations under the License.
15  */
16 
17 package com.android.server.usb;
18 
19 import static com.android.internal.usb.DumpUtils.writeDevice;
20 import static com.android.internal.util.dump.DumpUtils.writeComponentName;
21 
22 import android.annotation.NonNull;
23 import android.annotation.Nullable;
24 import android.content.ComponentName;
25 import android.content.Context;
26 import android.hardware.usb.UsbConstants;
27 import android.hardware.usb.UsbDevice;
28 import android.os.Bundle;
29 import android.os.ParcelFileDescriptor;
30 import android.service.ServiceProtoEnums;
31 import android.service.usb.UsbConnectionRecordProto;
32 import android.service.usb.UsbHostManagerProto;
33 import android.service.usb.UsbIsHeadsetProto;
34 import android.text.TextUtils;
35 import android.util.ArrayMap;
36 import android.util.Slog;
37 import android.util.StatsLog;
38 
39 import com.android.internal.annotations.GuardedBy;
40 import com.android.internal.util.IndentingPrintWriter;
41 import com.android.internal.util.dump.DualDumpOutputStream;
42 import com.android.server.usb.descriptors.UsbDescriptor;
43 import com.android.server.usb.descriptors.UsbDescriptorParser;
44 import com.android.server.usb.descriptors.UsbDeviceDescriptor;
45 import com.android.server.usb.descriptors.UsbInterfaceDescriptor;
46 import com.android.server.usb.descriptors.report.TextReportCanvas;
47 import com.android.server.usb.descriptors.tree.UsbDescriptorsTree;
48 
49 import java.text.SimpleDateFormat;
50 import java.util.Date;
51 import java.util.HashMap;
52 import java.util.LinkedList;
53 
54 /**
55  * UsbHostManager manages USB state in host mode.
56  */
57 public class UsbHostManager {
58     private static final String TAG = UsbHostManager.class.getSimpleName();
59     private static final boolean DEBUG = false;
60     private static final int LINUX_FOUNDATION_VID = 0x1d6b;
61 
62     private final Context mContext;
63 
64     // USB busses to exclude from USB host support
65     private final String[] mHostBlacklist;
66 
67     private final UsbAlsaManager mUsbAlsaManager;
68     private final UsbSettingsManager mSettingsManager;
69 
70     private final Object mLock = new Object();
71     @GuardedBy("mLock")
72     // contains all connected USB devices
73     private final HashMap<String, UsbDevice> mDevices = new HashMap<>();
74 
75     private Object mSettingsLock = new Object();
76     @GuardedBy("mSettingsLock")
77     private UsbProfileGroupSettingsManager mCurrentSettings;
78 
79     private Object mHandlerLock = new Object();
80     @GuardedBy("mHandlerLock")
81     private ComponentName mUsbDeviceConnectionHandler;
82 
83     /*
84      * Member used for tracking connections & disconnections
85      */
86     static final SimpleDateFormat sFormat = new SimpleDateFormat("MM-dd HH:mm:ss:SSS");
87     private static final int MAX_CONNECT_RECORDS = 32;
88     private int mNumConnects;    // TOTAL # of connect/disconnect
89     private final LinkedList<ConnectionRecord> mConnections = new LinkedList<ConnectionRecord>();
90     private ConnectionRecord mLastConnect;
91     private final ArrayMap<String, ConnectionRecord> mConnected = new ArrayMap<>();
92 
93     /*
94      * ConnectionRecord
95      * Stores connection/disconnection data.
96      */
97     class ConnectionRecord {
98         long mTimestamp;        // Same time-base as system log.
99         String mDeviceAddress;
100 
101         static final int CONNECT = ServiceProtoEnums.USB_CONNECTION_RECORD_MODE_CONNECT; // 0
102         static final int CONNECT_BADPARSE =
103                 ServiceProtoEnums.USB_CONNECTION_RECORD_MODE_CONNECT_BADPARSE; // 1
104         static final int CONNECT_BADDEVICE =
105                 ServiceProtoEnums.USB_CONNECTION_RECORD_MODE_CONNECT_BADDEVICE; // 2
106         static final int DISCONNECT =
107                 ServiceProtoEnums.USB_CONNECTION_RECORD_MODE_DISCONNECT; // -1
108 
109         final int mMode;
110         final byte[] mDescriptors;
111 
ConnectionRecord(String deviceAddress, int mode, byte[] descriptors)112         ConnectionRecord(String deviceAddress, int mode, byte[] descriptors) {
113             mTimestamp = System.currentTimeMillis();
114             mDeviceAddress = deviceAddress;
115             mMode = mode;
116             mDescriptors = descriptors;
117         }
118 
formatTime()119         private String formatTime() {
120             return (new StringBuilder(sFormat.format(new Date(mTimestamp)))).toString();
121         }
122 
dump(@onNull DualDumpOutputStream dump, String idName, long id)123         void dump(@NonNull DualDumpOutputStream dump, String idName, long id) {
124             long token = dump.start(idName, id);
125 
126             dump.write("device_address", UsbConnectionRecordProto.DEVICE_ADDRESS, mDeviceAddress);
127             dump.write("mode", UsbConnectionRecordProto.MODE, mMode);
128             dump.write("timestamp", UsbConnectionRecordProto.TIMESTAMP, mTimestamp);
129 
130             if (mMode != DISCONNECT) {
131                 UsbDescriptorParser parser = new UsbDescriptorParser(mDeviceAddress, mDescriptors);
132 
133                 UsbDeviceDescriptor deviceDescriptor = parser.getDeviceDescriptor();
134 
135                 dump.write("manufacturer", UsbConnectionRecordProto.MANUFACTURER,
136                         deviceDescriptor.getVendorID());
137                 dump.write("product", UsbConnectionRecordProto.PRODUCT,
138                         deviceDescriptor.getProductID());
139                 long isHeadSetToken = dump.start("is_headset", UsbConnectionRecordProto.IS_HEADSET);
140                 dump.write("in", UsbIsHeadsetProto.IN, parser.isInputHeadset());
141                 dump.write("out", UsbIsHeadsetProto.OUT, parser.isOutputHeadset());
142                 dump.end(isHeadSetToken);
143             }
144 
145             dump.end(token);
146         }
147 
dumpShort(IndentingPrintWriter pw)148         void dumpShort(IndentingPrintWriter pw) {
149             if (mMode != DISCONNECT) {
150                 pw.println(formatTime() + " Connect " + mDeviceAddress + " mode:" + mMode);
151                 UsbDescriptorParser parser = new UsbDescriptorParser(mDeviceAddress, mDescriptors);
152 
153                 UsbDeviceDescriptor deviceDescriptor = parser.getDeviceDescriptor();
154 
155                 pw.println("manfacturer:0x" + Integer.toHexString(deviceDescriptor.getVendorID())
156                         + " product:" + Integer.toHexString(deviceDescriptor.getProductID()));
157                 pw.println("isHeadset[in: " + parser.isInputHeadset()
158                         + " , out: " + parser.isOutputHeadset() + "]");
159             } else {
160                 pw.println(formatTime() + " Disconnect " + mDeviceAddress);
161             }
162         }
163 
dumpTree(IndentingPrintWriter pw)164         void dumpTree(IndentingPrintWriter pw) {
165             if (mMode != DISCONNECT) {
166                 pw.println(formatTime() + " Connect " + mDeviceAddress + " mode:" + mMode);
167                 UsbDescriptorParser parser = new UsbDescriptorParser(mDeviceAddress, mDescriptors);
168                 StringBuilder stringBuilder = new StringBuilder();
169                 UsbDescriptorsTree descriptorTree = new UsbDescriptorsTree();
170                 descriptorTree.parse(parser);
171                 descriptorTree.report(new TextReportCanvas(parser, stringBuilder));
172 
173                 stringBuilder.append("isHeadset[in: " + parser.isInputHeadset()
174                         + " , out: " + parser.isOutputHeadset() + "]");
175                 pw.println(stringBuilder.toString());
176             } else {
177                 pw.println(formatTime() + " Disconnect " + mDeviceAddress);
178             }
179         }
180 
dumpList(IndentingPrintWriter pw)181         void dumpList(IndentingPrintWriter pw) {
182             if (mMode != DISCONNECT) {
183                 pw.println(formatTime() + " Connect " + mDeviceAddress + " mode:" + mMode);
184                 UsbDescriptorParser parser = new UsbDescriptorParser(mDeviceAddress, mDescriptors);
185                 StringBuilder stringBuilder = new StringBuilder();
186                 TextReportCanvas canvas = new TextReportCanvas(parser, stringBuilder);
187                 for (UsbDescriptor descriptor : parser.getDescriptors()) {
188                     descriptor.report(canvas);
189                 }
190                 pw.println(stringBuilder.toString());
191 
192                 pw.println("isHeadset[in: " + parser.isInputHeadset()
193                         + " , out: " + parser.isOutputHeadset() + "]");
194             } else {
195                 pw.println(formatTime() + " Disconnect " + mDeviceAddress);
196             }
197         }
198 
199         private static final int kDumpBytesPerLine = 16;
200 
dumpRaw(IndentingPrintWriter pw)201         void dumpRaw(IndentingPrintWriter pw) {
202             if (mMode != DISCONNECT) {
203                 pw.println(formatTime() + " Connect " + mDeviceAddress + " mode:" + mMode);
204                 int length = mDescriptors.length;
205                 pw.println("Raw Descriptors " + length + " bytes");
206                 int dataOffset = 0;
207                 for (int line = 0; line < length / kDumpBytesPerLine; line++) {
208                     StringBuilder sb = new StringBuilder();
209                     for (int offset = 0; offset < kDumpBytesPerLine; offset++) {
210                         sb.append("0x")
211                             .append(String.format("0x%02X", mDescriptors[dataOffset++]))
212                             .append(" ");
213                     }
214                     pw.println(sb.toString());
215                 }
216 
217                 // remainder
218                 StringBuilder sb = new StringBuilder();
219                 while (dataOffset < length) {
220                     sb.append("0x")
221                         .append(String.format("0x%02X", mDescriptors[dataOffset++]))
222                         .append(" ");
223                 }
224                 pw.println(sb.toString());
225             } else {
226                 pw.println(formatTime() + " Disconnect " + mDeviceAddress);
227             }
228         }
229     }
230 
231     /*
232      * UsbHostManager
233      */
UsbHostManager(Context context, UsbAlsaManager alsaManager, UsbSettingsManager settingsManager)234     public UsbHostManager(Context context, UsbAlsaManager alsaManager,
235             UsbSettingsManager settingsManager) {
236         mContext = context;
237 
238         mHostBlacklist = context.getResources().getStringArray(
239                 com.android.internal.R.array.config_usbHostBlacklist);
240         mUsbAlsaManager = alsaManager;
241         mSettingsManager = settingsManager;
242         String deviceConnectionHandler = context.getResources().getString(
243                 com.android.internal.R.string.config_UsbDeviceConnectionHandling_component);
244         if (!TextUtils.isEmpty(deviceConnectionHandler)) {
245             setUsbDeviceConnectionHandler(ComponentName.unflattenFromString(
246                     deviceConnectionHandler));
247         }
248     }
249 
setCurrentUserSettings(UsbProfileGroupSettingsManager settings)250     public void setCurrentUserSettings(UsbProfileGroupSettingsManager settings) {
251         synchronized (mSettingsLock) {
252             mCurrentSettings = settings;
253         }
254     }
255 
getCurrentUserSettings()256     private UsbProfileGroupSettingsManager getCurrentUserSettings() {
257         synchronized (mSettingsLock) {
258             return mCurrentSettings;
259         }
260     }
261 
setUsbDeviceConnectionHandler(@ullable ComponentName usbDeviceConnectionHandler)262     public void setUsbDeviceConnectionHandler(@Nullable ComponentName usbDeviceConnectionHandler) {
263         synchronized (mHandlerLock) {
264             mUsbDeviceConnectionHandler = usbDeviceConnectionHandler;
265         }
266     }
267 
getUsbDeviceConnectionHandler()268     private @Nullable ComponentName getUsbDeviceConnectionHandler() {
269         synchronized (mHandlerLock) {
270             return mUsbDeviceConnectionHandler;
271         }
272     }
273 
isBlackListed(String deviceAddress)274     private boolean isBlackListed(String deviceAddress) {
275         int count = mHostBlacklist.length;
276         for (int i = 0; i < count; i++) {
277             if (deviceAddress.startsWith(mHostBlacklist[i])) {
278                 return true;
279             }
280         }
281         return false;
282     }
283 
284     /* returns true if the USB device should not be accessible by applications */
isBlackListed(int clazz, int subClass)285     private boolean isBlackListed(int clazz, int subClass) {
286         // blacklist hubs
287         if (clazz == UsbConstants.USB_CLASS_HUB) return true;
288 
289         // blacklist HID boot devices (mouse and keyboard)
290         return clazz == UsbConstants.USB_CLASS_HID
291                 && subClass == UsbConstants.USB_INTERFACE_SUBCLASS_BOOT;
292 
293     }
294 
addConnectionRecord(String deviceAddress, int mode, byte[] rawDescriptors)295     private void addConnectionRecord(String deviceAddress, int mode, byte[] rawDescriptors) {
296         mNumConnects++;
297         while (mConnections.size() >= MAX_CONNECT_RECORDS) {
298             mConnections.removeFirst();
299         }
300         ConnectionRecord rec =
301                 new ConnectionRecord(deviceAddress, mode, rawDescriptors);
302         mConnections.add(rec);
303         if (mode != ConnectionRecord.DISCONNECT) {
304             mLastConnect = rec;
305         }
306         if (mode == ConnectionRecord.CONNECT) {
307             mConnected.put(deviceAddress, rec);
308         } else if (mode == ConnectionRecord.DISCONNECT) {
309             mConnected.remove(deviceAddress);
310         }
311     }
312 
logUsbDevice(UsbDescriptorParser descriptorParser)313     private void logUsbDevice(UsbDescriptorParser descriptorParser) {
314         int vid = 0;
315         int pid = 0;
316         String mfg = "<unknown>";
317         String product = "<unknown>";
318         String version = "<unknown>";
319         String serial = "<unknown>";
320 
321         UsbDeviceDescriptor deviceDescriptor = descriptorParser.getDeviceDescriptor();
322         if (deviceDescriptor != null) {
323             vid = deviceDescriptor.getVendorID();
324             pid = deviceDescriptor.getProductID();
325             mfg = deviceDescriptor.getMfgString(descriptorParser);
326             product = deviceDescriptor.getProductString(descriptorParser);
327             version = deviceDescriptor.getDeviceReleaseString();
328             serial = deviceDescriptor.getSerialString(descriptorParser);
329         }
330 
331         if (vid == LINUX_FOUNDATION_VID) {
332             return;  // don't care about OS-constructed virtual USB devices.
333         }
334         boolean hasAudio = descriptorParser.hasAudioInterface();
335         boolean hasHid = descriptorParser.hasHIDInterface();
336         boolean hasStorage = descriptorParser.hasStorageInterface();
337 
338         String attachedString = "USB device attached: ";
339         attachedString += String.format("vidpid %04x:%04x", vid, pid);
340         attachedString += String.format(" mfg/product/ver/serial %s/%s/%s/%s",
341                                         mfg, product, version, serial);
342         attachedString += String.format(" hasAudio/HID/Storage: %b/%b/%b",
343                                         hasAudio, hasHid, hasStorage);
344         Slog.d(TAG, attachedString);
345     }
346 
347     /* Called from JNI in monitorUsbHostBus() to report new USB devices
348        Returns true if successful, i.e. the USB Audio device descriptors are
349        correctly parsed and the unique device is added to the audio device list.
350      */
351     @SuppressWarnings("unused")
usbDeviceAdded(String deviceAddress, int deviceClass, int deviceSubclass, byte[] descriptors)352     private boolean usbDeviceAdded(String deviceAddress, int deviceClass, int deviceSubclass,
353             byte[] descriptors) {
354         if (DEBUG) {
355             Slog.d(TAG, "usbDeviceAdded(" + deviceAddress + ") - start");
356         }
357 
358         if (isBlackListed(deviceAddress)) {
359             if (DEBUG) {
360                 Slog.d(TAG, "device address is black listed");
361             }
362             return false;
363         }
364 
365         if (isBlackListed(deviceClass, deviceSubclass)) {
366             if (DEBUG) {
367                 Slog.d(TAG, "device class is black listed");
368             }
369             return false;
370         }
371 
372         UsbDescriptorParser parser = new UsbDescriptorParser(deviceAddress, descriptors);
373         if (deviceClass == UsbConstants.USB_CLASS_PER_INTERFACE
374                 && !checkUsbInterfacesBlackListed(parser)) {
375             return false;
376         }
377 
378         // Potentially can block as it may read data from the USB device.
379         logUsbDevice(parser);
380 
381         synchronized (mLock) {
382             if (mDevices.get(deviceAddress) != null) {
383                 Slog.w(TAG, "device already on mDevices list: " + deviceAddress);
384                 //TODO If this is the same peripheral as is being connected, replace
385                 // it with the new connection.
386                 return false;
387             }
388 
389             UsbDevice.Builder newDeviceBuilder = parser.toAndroidUsbDevice();
390             if (newDeviceBuilder == null) {
391                 Slog.e(TAG, "Couldn't create UsbDevice object.");
392                 // Tracking
393                 addConnectionRecord(deviceAddress, ConnectionRecord.CONNECT_BADDEVICE,
394                         parser.getRawDescriptors());
395             } else {
396                 UsbSerialReader serialNumberReader = new UsbSerialReader(mContext, mSettingsManager,
397                         newDeviceBuilder.serialNumber);
398                 UsbDevice newDevice = newDeviceBuilder.build(serialNumberReader);
399                 serialNumberReader.setDevice(newDevice);
400 
401                 mDevices.put(deviceAddress, newDevice);
402                 Slog.d(TAG, "Added device " + newDevice);
403 
404                 // It is fine to call this only for the current user as all broadcasts are
405                 // sent to all profiles of the user and the dialogs should only show once.
406                 ComponentName usbDeviceConnectionHandler = getUsbDeviceConnectionHandler();
407                 if (usbDeviceConnectionHandler == null) {
408                     getCurrentUserSettings().deviceAttached(newDevice);
409                 } else {
410                     getCurrentUserSettings().deviceAttachedForFixedHandler(newDevice,
411                             usbDeviceConnectionHandler);
412                 }
413 
414                 mUsbAlsaManager.usbDeviceAdded(deviceAddress, newDevice, parser);
415 
416                 // Tracking
417                 addConnectionRecord(deviceAddress, ConnectionRecord.CONNECT,
418                         parser.getRawDescriptors());
419 
420                 // Stats collection
421                 StatsLog.write(StatsLog.USB_DEVICE_ATTACHED, newDevice.getVendorId(),
422                         newDevice.getProductId(), parser.hasAudioInterface(),
423                         parser.hasHIDInterface(), parser.hasStorageInterface(),
424                         StatsLog.USB_DEVICE_ATTACHED__STATE__STATE_CONNECTED, 0);
425             }
426         }
427 
428         if (DEBUG) {
429             Slog.d(TAG, "beginUsbDeviceAdded(" + deviceAddress + ") end");
430         }
431 
432         return true;
433     }
434 
435     /* Called from JNI in monitorUsbHostBus to report USB device removal */
436     @SuppressWarnings("unused")
usbDeviceRemoved(String deviceAddress)437     private void usbDeviceRemoved(String deviceAddress) {
438         if (DEBUG) {
439             Slog.d(TAG, "usbDeviceRemoved(" + deviceAddress + ") end");
440         }
441 
442         synchronized (mLock) {
443             UsbDevice device = mDevices.remove(deviceAddress);
444             if (device != null) {
445                 Slog.d(TAG, "Removed device at " + deviceAddress + ": " + device.getProductName());
446                 mUsbAlsaManager.usbDeviceRemoved(deviceAddress);
447                 mSettingsManager.usbDeviceRemoved(device);
448                 getCurrentUserSettings().usbDeviceRemoved(device);
449                 ConnectionRecord current = mConnected.get(deviceAddress);
450                 // Tracking
451                 addConnectionRecord(deviceAddress, ConnectionRecord.DISCONNECT, null);
452 
453                 if (current != null) {
454                     UsbDescriptorParser parser = new UsbDescriptorParser(deviceAddress,
455                             current.mDescriptors);
456                         // Stats collection
457                     StatsLog.write(StatsLog.USB_DEVICE_ATTACHED, device.getVendorId(),
458                             device.getProductId(), parser.hasAudioInterface(),
459                             parser.hasHIDInterface(),  parser.hasStorageInterface(),
460                             StatsLog.USB_DEVICE_ATTACHED__STATE__STATE_DISCONNECTED,
461                             System.currentTimeMillis() - current.mTimestamp);
462                 }
463             } else {
464                 Slog.d(TAG, "Removed device at " + deviceAddress + " was already gone");
465             }
466         }
467     }
468 
systemReady()469     public void systemReady() {
470         synchronized (mLock) {
471             // Create a thread to call into native code to wait for USB host events.
472             // This thread will call us back on usbDeviceAdded and usbDeviceRemoved.
473             Runnable runnable = this::monitorUsbHostBus;
474             new Thread(null, runnable, "UsbService host thread").start();
475         }
476     }
477 
478     /* Returns a list of all currently attached USB devices */
getDeviceList(Bundle devices)479     public void getDeviceList(Bundle devices) {
480         synchronized (mLock) {
481             for (String name : mDevices.keySet()) {
482                 devices.putParcelable(name, mDevices.get(name));
483             }
484         }
485     }
486 
487     /* Opens the specified USB device */
openDevice(String deviceAddress, UsbUserSettingsManager settings, String packageName, int pid, int uid)488     public ParcelFileDescriptor openDevice(String deviceAddress, UsbUserSettingsManager settings,
489             String packageName, int pid, int uid) {
490         synchronized (mLock) {
491             if (isBlackListed(deviceAddress)) {
492                 throw new SecurityException("USB device is on a restricted bus");
493             }
494             UsbDevice device = mDevices.get(deviceAddress);
495             if (device == null) {
496                 // if it is not in mDevices, it either does not exist or is blacklisted
497                 throw new IllegalArgumentException(
498                         "device " + deviceAddress + " does not exist or is restricted");
499             }
500 
501             settings.checkPermission(device, packageName, pid, uid);
502             return nativeOpenDevice(deviceAddress);
503         }
504     }
505 
506     /**
507      * Dump out various information about the state of USB device connections.
508      */
dump(DualDumpOutputStream dump, String idName, long id)509     public void dump(DualDumpOutputStream dump, String idName, long id) {
510         long token = dump.start(idName, id);
511 
512         synchronized (mHandlerLock) {
513             if (mUsbDeviceConnectionHandler != null) {
514                 writeComponentName(dump, "default_usb_host_connection_handler",
515                         UsbHostManagerProto.DEFAULT_USB_HOST_CONNECTION_HANDLER,
516                         mUsbDeviceConnectionHandler);
517             }
518         }
519         synchronized (mLock) {
520             for (String name : mDevices.keySet()) {
521                 writeDevice(dump, "devices", UsbHostManagerProto.DEVICES, mDevices.get(name));
522             }
523 
524             dump.write("num_connects", UsbHostManagerProto.NUM_CONNECTS, mNumConnects);
525 
526             for (ConnectionRecord rec : mConnections) {
527                 rec.dump(dump, "connections", UsbHostManagerProto.CONNECTIONS);
528             }
529         }
530 
531         dump.end(token);
532     }
533 
534     /**
535      * Dump various descriptor data.
536      */
dumpDescriptors(IndentingPrintWriter pw, String[] args)537     public void dumpDescriptors(IndentingPrintWriter pw, String[] args) {
538         if (mLastConnect != null) {
539             pw.println("Last Connected USB Device:");
540             if (args.length <= 1 || args[1].equals("-dump-short")) {
541                 mLastConnect.dumpShort(pw);
542             } else if (args[1].equals("-dump-tree")) {
543                 mLastConnect.dumpTree(pw);
544             } else if (args[1].equals("-dump-list")) {
545                 mLastConnect.dumpList(pw);
546             }  else if (args[1].equals("-dump-raw")) {
547                 mLastConnect.dumpRaw(pw);
548             }
549         } else {
550             pw.println("No USB Devices have been connected.");
551         }
552     }
553 
checkUsbInterfacesBlackListed(UsbDescriptorParser parser)554     private boolean checkUsbInterfacesBlackListed(UsbDescriptorParser parser) {
555         // Device class needs to be obtained through the device interface.  Ignore device only
556         // if ALL interfaces are black-listed.
557         boolean shouldIgnoreDevice = false;
558         for (UsbDescriptor descriptor: parser.getDescriptors()) {
559             if (!(descriptor instanceof UsbInterfaceDescriptor)) {
560                 continue;
561             }
562             UsbInterfaceDescriptor iface = (UsbInterfaceDescriptor) descriptor;
563             shouldIgnoreDevice = isBlackListed(iface.getUsbClass(), iface.getUsbSubclass());
564             if (!shouldIgnoreDevice) {
565                 break;
566             }
567         }
568         if (shouldIgnoreDevice) {
569             if (DEBUG) {
570                 Slog.d(TAG, "usb interface class is black listed");
571             }
572             return false;
573         }
574         return true;
575     }
576 
monitorUsbHostBus()577     private native void monitorUsbHostBus();
nativeOpenDevice(String deviceAddress)578     private native ParcelFileDescriptor nativeOpenDevice(String deviceAddress);
579 }
580