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