1 /*
2  * Copyright (C) 2010 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 
18 package android.hardware.usb;
19 
20 import com.android.internal.util.Preconditions;
21 
22 import android.app.PendingIntent;
23 import android.content.Context;
24 import android.os.Bundle;
25 import android.os.ParcelFileDescriptor;
26 import android.os.Process;
27 import android.os.RemoteException;
28 import android.util.Log;
29 
30 import java.util.HashMap;
31 
32 /**
33  * This class allows you to access the state of USB and communicate with USB devices.
34  * Currently only host mode is supported in the public API.
35  *
36  * <p>You can obtain an instance of this class by calling
37  * {@link android.content.Context#getSystemService(java.lang.String) Context.getSystemService()}.
38  *
39  * {@samplecode
40  * UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE);}
41  *
42  * <div class="special reference">
43  * <h3>Developer Guides</h3>
44  * <p>For more information about communicating with USB hardware, read the
45  * <a href="{@docRoot}guide/topics/usb/index.html">USB</a> developer guide.</p>
46  * </div>
47  */
48 public class UsbManager {
49     private static final String TAG = "UsbManager";
50 
51    /**
52      * Broadcast Action:  A sticky broadcast for USB state change events when in device mode.
53      *
54      * This is a sticky broadcast for clients that includes USB connected/disconnected state,
55      * <ul>
56      * <li> {@link #USB_CONNECTED} boolean indicating whether USB is connected or disconnected.
57      * <li> {@link #USB_HOST_CONNECTED} boolean indicating whether USB is connected or
58      *     disconnected as host.
59      * <li> {@link #USB_CONFIGURED} boolean indicating whether USB is configured.
60      * currently zero if not configured, one for configured.
61      * <li> {@link #USB_FUNCTION_ADB} boolean extra indicating whether the
62      * adb function is enabled
63      * <li> {@link #USB_FUNCTION_RNDIS} boolean extra indicating whether the
64      * RNDIS ethernet function is enabled
65      * <li> {@link #USB_FUNCTION_MTP} boolean extra indicating whether the
66      * MTP function is enabled
67      * <li> {@link #USB_FUNCTION_PTP} boolean extra indicating whether the
68      * PTP function is enabled
69      * <li> {@link #USB_FUNCTION_PTP} boolean extra indicating whether the
70      * accessory function is enabled
71      * <li> {@link #USB_FUNCTION_AUDIO_SOURCE} boolean extra indicating whether the
72      * audio source function is enabled
73      * <li> {@link #USB_FUNCTION_MIDI} boolean extra indicating whether the
74      * MIDI function is enabled
75      * </ul>
76      * If the sticky intent has not been found, that indicates USB is disconnected,
77      * USB is not configued, MTP function is enabled, and all the other functions are disabled.
78      *
79      * {@hide}
80      */
81     public static final String ACTION_USB_STATE =
82             "android.hardware.usb.action.USB_STATE";
83 
84     /**
85      * Broadcast Action: A broadcast for USB port changes.
86      *
87      * This intent is sent when a USB port is added, removed, or changes state.
88      * <ul>
89      * <li> {@link #EXTRA_PORT} containing the {@link android.hardware.usb.UsbPort}
90      * for the port.
91      * <li> {@link #EXTRA_PORT_STATUS} containing the {@link android.hardware.usb.UsbPortStatus}
92      * for the port, or null if the port has been removed
93      * </ul>
94      *
95      * @hide
96      */
97     public static final String ACTION_USB_PORT_CHANGED =
98             "android.hardware.usb.action.USB_PORT_CHANGED";
99 
100    /**
101      * Broadcast Action:  A broadcast for USB device attached event.
102      *
103      * This intent is sent when a USB device is attached to the USB bus when in host mode.
104      * <ul>
105      * <li> {@link #EXTRA_DEVICE} containing the {@link android.hardware.usb.UsbDevice}
106      * for the attached device
107      * </ul>
108      */
109     public static final String ACTION_USB_DEVICE_ATTACHED =
110             "android.hardware.usb.action.USB_DEVICE_ATTACHED";
111 
112    /**
113      * Broadcast Action:  A broadcast for USB device detached event.
114      *
115      * This intent is sent when a USB device is detached from the USB bus when in host mode.
116      * <ul>
117      * <li> {@link #EXTRA_DEVICE} containing the {@link android.hardware.usb.UsbDevice}
118      * for the detached device
119      * </ul>
120      */
121     public static final String ACTION_USB_DEVICE_DETACHED =
122             "android.hardware.usb.action.USB_DEVICE_DETACHED";
123 
124    /**
125      * Broadcast Action:  A broadcast for USB accessory attached event.
126      *
127      * This intent is sent when a USB accessory is attached.
128      * <ul>
129      * <li> {@link #EXTRA_ACCESSORY} containing the {@link android.hardware.usb.UsbAccessory}
130      * for the attached accessory
131      * </ul>
132      */
133     public static final String ACTION_USB_ACCESSORY_ATTACHED =
134             "android.hardware.usb.action.USB_ACCESSORY_ATTACHED";
135 
136    /**
137      * Broadcast Action:  A broadcast for USB accessory detached event.
138      *
139      * This intent is sent when a USB accessory is detached.
140      * <ul>
141      * <li> {@link #EXTRA_ACCESSORY} containing the {@link UsbAccessory}
142      * for the attached accessory that was detached
143      * </ul>
144      */
145     public static final String ACTION_USB_ACCESSORY_DETACHED =
146             "android.hardware.usb.action.USB_ACCESSORY_DETACHED";
147 
148     /**
149      * Boolean extra indicating whether USB is connected or disconnected.
150      * Used in extras for the {@link #ACTION_USB_STATE} broadcast.
151      *
152      * {@hide}
153      */
154     public static final String USB_CONNECTED = "connected";
155 
156     /**
157      * Boolean extra indicating whether USB is connected or disconnected as host.
158      * Used in extras for the {@link #ACTION_USB_STATE} broadcast.
159      *
160      * {@hide}
161      */
162     public static final String USB_HOST_CONNECTED = "host_connected";
163 
164     /**
165      * Boolean extra indicating whether USB is configured.
166      * Used in extras for the {@link #ACTION_USB_STATE} broadcast.
167      *
168      * {@hide}
169      */
170     public static final String USB_CONFIGURED = "configured";
171 
172     /**
173      * Boolean extra indicating whether confidential user data, such as photos, should be
174      * made available on the USB connection. This variable will only be set when the user
175      * has explicitly asked for this data to be unlocked.
176      * Used in extras for the {@link #ACTION_USB_STATE} broadcast.
177      *
178      * {@hide}
179      */
180     public static final String USB_DATA_UNLOCKED = "unlocked";
181 
182     /**
183      * A placeholder indicating that no USB function is being specified.
184      * Used to distinguish between selecting no function vs. the default function in
185      * {@link #setCurrentFunction(String)}.
186      *
187      * {@hide}
188      */
189     public static final String USB_FUNCTION_NONE = "none";
190 
191     /**
192      * Name of the adb USB function.
193      * Used in extras for the {@link #ACTION_USB_STATE} broadcast
194      *
195      * {@hide}
196      */
197     public static final String USB_FUNCTION_ADB = "adb";
198 
199     /**
200      * Name of the RNDIS ethernet USB function.
201      * Used in extras for the {@link #ACTION_USB_STATE} broadcast
202      *
203      * {@hide}
204      */
205     public static final String USB_FUNCTION_RNDIS = "rndis";
206 
207     /**
208      * Name of the MTP USB function.
209      * Used in extras for the {@link #ACTION_USB_STATE} broadcast
210      *
211      * {@hide}
212      */
213     public static final String USB_FUNCTION_MTP = "mtp";
214 
215     /**
216      * Name of the PTP USB function.
217      * Used in extras for the {@link #ACTION_USB_STATE} broadcast
218      *
219      * {@hide}
220      */
221     public static final String USB_FUNCTION_PTP = "ptp";
222 
223     /**
224      * Name of the audio source USB function.
225      * Used in extras for the {@link #ACTION_USB_STATE} broadcast
226      *
227      * {@hide}
228      */
229     public static final String USB_FUNCTION_AUDIO_SOURCE = "audio_source";
230 
231     /**
232      * Name of the MIDI USB function.
233      * Used in extras for the {@link #ACTION_USB_STATE} broadcast
234      *
235      * {@hide}
236      */
237     public static final String USB_FUNCTION_MIDI = "midi";
238 
239     /**
240      * Name of the Accessory USB function.
241      * Used in extras for the {@link #ACTION_USB_STATE} broadcast
242      *
243      * {@hide}
244      */
245     public static final String USB_FUNCTION_ACCESSORY = "accessory";
246 
247     /**
248      * Name of extra for {@link #ACTION_USB_PORT_CHANGED}
249      * containing the {@link UsbPort} object for the port.
250      *
251      * @hide
252      */
253     public static final String EXTRA_PORT = "port";
254 
255     /**
256      * Name of extra for {@link #ACTION_USB_PORT_CHANGED}
257      * containing the {@link UsbPortStatus} object for the port, or null if the port
258      * was removed.
259      *
260      * @hide
261      */
262     public static final String EXTRA_PORT_STATUS = "portStatus";
263 
264     /**
265      * Name of extra for {@link #ACTION_USB_DEVICE_ATTACHED} and
266      * {@link #ACTION_USB_DEVICE_DETACHED} broadcasts
267      * containing the {@link UsbDevice} object for the device.
268      */
269     public static final String EXTRA_DEVICE = "device";
270 
271     /**
272      * Name of extra for {@link #ACTION_USB_ACCESSORY_ATTACHED} and
273      * {@link #ACTION_USB_ACCESSORY_DETACHED} broadcasts
274      * containing the {@link UsbAccessory} object for the accessory.
275      */
276     public static final String EXTRA_ACCESSORY = "accessory";
277 
278     /**
279      * Name of extra added to the {@link android.app.PendingIntent}
280      * passed into {@link #requestPermission(UsbDevice, PendingIntent)}
281      * or {@link #requestPermission(UsbAccessory, PendingIntent)}
282      * containing a boolean value indicating whether the user granted permission or not.
283      */
284     public static final String EXTRA_PERMISSION_GRANTED = "permission";
285 
286     private final Context mContext;
287     private final IUsbManager mService;
288 
289     /**
290      * {@hide}
291      */
UsbManager(Context context, IUsbManager service)292     public UsbManager(Context context, IUsbManager service) {
293         mContext = context;
294         mService = service;
295     }
296 
297     /**
298      * Returns a HashMap containing all USB devices currently attached.
299      * USB device name is the key for the returned HashMap.
300      * The result will be empty if no devices are attached, or if
301      * USB host mode is inactive or unsupported.
302      *
303      * @return HashMap containing all connected USB devices.
304      */
getDeviceList()305     public HashMap<String,UsbDevice> getDeviceList() {
306         Bundle bundle = new Bundle();
307         try {
308             mService.getDeviceList(bundle);
309             HashMap<String,UsbDevice> result = new HashMap<String,UsbDevice>();
310             for (String name : bundle.keySet()) {
311                 result.put(name, (UsbDevice)bundle.get(name));
312             }
313             return result;
314         } catch (RemoteException e) {
315             throw e.rethrowFromSystemServer();
316         }
317     }
318 
319     /**
320      * Opens the device so it can be used to send and receive
321      * data using {@link android.hardware.usb.UsbRequest}.
322      *
323      * @param device the device to open
324      * @return a {@link UsbDeviceConnection}, or {@code null} if open failed
325      */
openDevice(UsbDevice device)326     public UsbDeviceConnection openDevice(UsbDevice device) {
327         try {
328             String deviceName = device.getDeviceName();
329             ParcelFileDescriptor pfd = mService.openDevice(deviceName);
330             if (pfd != null) {
331                 UsbDeviceConnection connection = new UsbDeviceConnection(device);
332                 boolean result = connection.open(deviceName, pfd);
333                 pfd.close();
334                 if (result) {
335                     return connection;
336                 }
337             }
338         } catch (Exception e) {
339             Log.e(TAG, "exception in UsbManager.openDevice", e);
340         }
341         return null;
342     }
343 
344     /**
345      * Returns a list of currently attached USB accessories.
346      * (in the current implementation there can be at most one)
347      *
348      * @return list of USB accessories, or null if none are attached.
349      */
getAccessoryList()350     public UsbAccessory[] getAccessoryList() {
351         try {
352             UsbAccessory accessory = mService.getCurrentAccessory();
353             if (accessory == null) {
354                 return null;
355             } else {
356                 return new UsbAccessory[] { accessory };
357             }
358         } catch (RemoteException e) {
359             throw e.rethrowFromSystemServer();
360         }
361     }
362 
363     /**
364      * Opens a file descriptor for reading and writing data to the USB accessory.
365      *
366      * @param accessory the USB accessory to open
367      * @return file descriptor, or null if the accessor could not be opened.
368      */
openAccessory(UsbAccessory accessory)369     public ParcelFileDescriptor openAccessory(UsbAccessory accessory) {
370         try {
371             return mService.openAccessory(accessory);
372         } catch (RemoteException e) {
373             throw e.rethrowFromSystemServer();
374         }
375     }
376 
377     /**
378      * Returns true if the caller has permission to access the device.
379      * Permission might have been granted temporarily via
380      * {@link #requestPermission(UsbDevice, PendingIntent)} or
381      * by the user choosing the caller as the default application for the device.
382      *
383      * @param device to check permissions for
384      * @return true if caller has permission
385      */
hasPermission(UsbDevice device)386     public boolean hasPermission(UsbDevice device) {
387         try {
388             return mService.hasDevicePermission(device);
389         } catch (RemoteException e) {
390             throw e.rethrowFromSystemServer();
391         }
392     }
393 
394     /**
395      * Returns true if the caller has permission to access the accessory.
396      * Permission might have been granted temporarily via
397      * {@link #requestPermission(UsbAccessory, PendingIntent)} or
398      * by the user choosing the caller as the default application for the accessory.
399      *
400      * @param accessory to check permissions for
401      * @return true if caller has permission
402      */
hasPermission(UsbAccessory accessory)403     public boolean hasPermission(UsbAccessory accessory) {
404         try {
405             return mService.hasAccessoryPermission(accessory);
406         } catch (RemoteException e) {
407             throw e.rethrowFromSystemServer();
408         }
409     }
410 
411     /**
412      * Requests temporary permission for the given package to access the device.
413      * This may result in a system dialog being displayed to the user
414      * if permission had not already been granted.
415      * Success or failure is returned via the {@link android.app.PendingIntent} pi.
416      * If successful, this grants the caller permission to access the device only
417      * until the device is disconnected.
418      *
419      * The following extras will be added to pi:
420      * <ul>
421      * <li> {@link #EXTRA_DEVICE} containing the device passed into this call
422      * <li> {@link #EXTRA_PERMISSION_GRANTED} containing boolean indicating whether
423      * permission was granted by the user
424      * </ul>
425      *
426      * @param device to request permissions for
427      * @param pi PendingIntent for returning result
428      */
requestPermission(UsbDevice device, PendingIntent pi)429     public void requestPermission(UsbDevice device, PendingIntent pi) {
430         try {
431             mService.requestDevicePermission(device, mContext.getPackageName(), pi);
432         } catch (RemoteException e) {
433             throw e.rethrowFromSystemServer();
434         }
435     }
436 
437     /**
438      * Requests temporary permission for the given package to access the accessory.
439      * This may result in a system dialog being displayed to the user
440      * if permission had not already been granted.
441      * Success or failure is returned via the {@link android.app.PendingIntent} pi.
442      * If successful, this grants the caller permission to access the accessory only
443      * until the device is disconnected.
444      *
445      * The following extras will be added to pi:
446      * <ul>
447      * <li> {@link #EXTRA_ACCESSORY} containing the accessory passed into this call
448      * <li> {@link #EXTRA_PERMISSION_GRANTED} containing boolean indicating whether
449      * permission was granted by the user
450      * </ul>
451      *
452      * @param accessory to request permissions for
453      * @param pi PendingIntent for returning result
454      */
requestPermission(UsbAccessory accessory, PendingIntent pi)455     public void requestPermission(UsbAccessory accessory, PendingIntent pi) {
456         try {
457             mService.requestAccessoryPermission(accessory, mContext.getPackageName(), pi);
458         } catch (RemoteException e) {
459             throw e.rethrowFromSystemServer();
460         }
461     }
462 
463     /**
464      * Grants permission for USB device without showing system dialog.
465      * Only system components can call this function.
466      * @param device to request permissions for
467      *
468      * {@hide}
469      */
grantPermission(UsbDevice device)470     public void grantPermission(UsbDevice device) {
471         try {
472             mService.grantDevicePermission(device, Process.myUid());
473         } catch (RemoteException e) {
474             throw e.rethrowFromSystemServer();
475         }
476     }
477 
478     /**
479      * Returns true if the specified USB function is currently enabled when in device mode.
480      * <p>
481      * USB functions represent interfaces which are published to the host to access
482      * services offered by the device.
483      * </p>
484      *
485      * @param function name of the USB function
486      * @return true if the USB function is enabled
487      *
488      * {@hide}
489      */
isFunctionEnabled(String function)490     public boolean isFunctionEnabled(String function) {
491         try {
492             return mService.isFunctionEnabled(function);
493         } catch (RemoteException e) {
494             throw e.rethrowFromSystemServer();
495         }
496     }
497 
498     /**
499      * Sets the current USB function when in device mode.
500      * <p>
501      * USB functions represent interfaces which are published to the host to access
502      * services offered by the device.
503      * </p><p>
504      * This method is intended to select among primary USB functions.  The system may
505      * automatically activate additional functions such as {@link #USB_FUNCTION_ADB}
506      * or {@link #USB_FUNCTION_ACCESSORY} based on other settings and states.
507      * </p><p>
508      * The allowed values are: {@link #USB_FUNCTION_NONE}, {@link #USB_FUNCTION_AUDIO_SOURCE},
509      * {@link #USB_FUNCTION_MIDI}, {@link #USB_FUNCTION_MTP}, {@link #USB_FUNCTION_PTP},
510      * or {@link #USB_FUNCTION_RNDIS}.
511      * </p><p>
512      * Note: This function is asynchronous and may fail silently without applying
513      * the requested changes.
514      * </p>
515      *
516      * @param function name of the USB function, or null to restore the default function
517      *
518      * {@hide}
519      */
setCurrentFunction(String function)520     public void setCurrentFunction(String function) {
521         try {
522             mService.setCurrentFunction(function);
523         } catch (RemoteException e) {
524             throw e.rethrowFromSystemServer();
525         }
526     }
527 
528     /**
529      * Sets whether USB data (for example, MTP exposed pictures) should be made available
530      * on the USB connection when in device mode. Unlocking usb data should only be done with
531      * user involvement, since exposing pictures or other data could leak sensitive
532      * user information.
533      *
534      * {@hide}
535      */
setUsbDataUnlocked(boolean unlocked)536     public void setUsbDataUnlocked(boolean unlocked) {
537         try {
538             mService.setUsbDataUnlocked(unlocked);
539         } catch (RemoteException e) {
540             throw e.rethrowFromSystemServer();
541         }
542     }
543 
544     /**
545      * Returns a list of physical USB ports on the device.
546      * <p>
547      * This list is guaranteed to contain all dual-role USB Type C ports but it might
548      * be missing other ports depending on whether the kernel USB drivers have been
549      * updated to publish all of the device's ports through the new "dual_role_usb"
550      * device class (which supports all types of ports despite its name).
551      * </p>
552      *
553      * @return The list of USB ports, or null if none.
554      *
555      * @hide
556      */
getPorts()557     public UsbPort[] getPorts() {
558         try {
559             return mService.getPorts();
560         } catch (RemoteException e) {
561             throw e.rethrowFromSystemServer();
562         }
563     }
564 
565     /**
566      * Gets the status of the specified USB port.
567      *
568      * @param port The port to query.
569      * @return The status of the specified USB port, or null if unknown.
570      *
571      * @hide
572      */
getPortStatus(UsbPort port)573     public UsbPortStatus getPortStatus(UsbPort port) {
574         Preconditions.checkNotNull(port, "port must not be null");
575 
576         try {
577             return mService.getPortStatus(port.getId());
578         } catch (RemoteException e) {
579             throw e.rethrowFromSystemServer();
580         }
581     }
582 
583     /**
584      * Sets the desired role combination of the port.
585      * <p>
586      * The supported role combinations depend on what is connected to the port and may be
587      * determined by consulting
588      * {@link UsbPortStatus#isRoleCombinationSupported UsbPortStatus.isRoleCombinationSupported}.
589      * </p><p>
590      * Note: This function is asynchronous and may fail silently without applying
591      * the requested changes.  If this function does cause a status change to occur then
592      * a {@link #ACTION_USB_PORT_CHANGED} broadcast will be sent.
593      * </p>
594      *
595      * @param powerRole The desired power role: {@link UsbPort#POWER_ROLE_SOURCE}
596      * or {@link UsbPort#POWER_ROLE_SINK}, or 0 if no power role.
597      * @param dataRole The desired data role: {@link UsbPort#DATA_ROLE_HOST}
598      * or {@link UsbPort#DATA_ROLE_DEVICE}, or 0 if no data role.
599      *
600      * @hide
601      */
setPortRoles(UsbPort port, int powerRole, int dataRole)602     public void setPortRoles(UsbPort port, int powerRole, int dataRole) {
603         Preconditions.checkNotNull(port, "port must not be null");
604         UsbPort.checkRoles(powerRole, dataRole);
605 
606         try {
607             mService.setPortRoles(port.getId(), powerRole, dataRole);
608         } catch (RemoteException e) {
609             throw e.rethrowFromSystemServer();
610         }
611     }
612 
613     /** @hide */
addFunction(String functions, String function)614     public static String addFunction(String functions, String function) {
615         if (USB_FUNCTION_NONE.equals(functions)) {
616             return function;
617         }
618         if (!containsFunction(functions, function)) {
619             if (functions.length() > 0) {
620                 functions += ",";
621             }
622             functions += function;
623         }
624         return functions;
625     }
626 
627     /** @hide */
removeFunction(String functions, String function)628     public static String removeFunction(String functions, String function) {
629         String[] split = functions.split(",");
630         for (int i = 0; i < split.length; i++) {
631             if (function.equals(split[i])) {
632                 split[i] = null;
633             }
634         }
635         if (split.length == 1 && split[0] == null) {
636             return USB_FUNCTION_NONE;
637         }
638         StringBuilder builder = new StringBuilder();
639         for (int i = 0; i < split.length; i++) {
640             String s = split[i];
641             if (s != null) {
642                 if (builder.length() > 0) {
643                     builder.append(",");
644                 }
645                 builder.append(s);
646             }
647         }
648         return builder.toString();
649     }
650 
651     /** @hide */
containsFunction(String functions, String function)652     public static boolean containsFunction(String functions, String function) {
653         int index = functions.indexOf(function);
654         if (index < 0) return false;
655         if (index > 0 && functions.charAt(index - 1) != ',') return false;
656         int charAfter = index + function.length();
657         if (charAfter < functions.length() && functions.charAt(charAfter) != ',') return false;
658         return true;
659     }
660 }
661