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