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