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 android.app.PendingIntent;
21import android.content.Context;
22import android.os.Bundle;
23import android.os.ParcelFileDescriptor;
24import android.os.RemoteException;
25import android.os.SystemProperties;
26import android.util.Log;
27
28import java.util.HashMap;
29
30/**
31 * This class allows you to access the state of USB and communicate with USB devices.
32 * Currently only host mode is supported in the public API.
33 *
34 * <p>You can obtain an instance of this class by calling
35 * {@link android.content.Context#getSystemService(java.lang.String) Context.getSystemService()}.
36 *
37 * {@samplecode
38 * UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE);}
39 *
40 * <div class="special reference">
41 * <h3>Developer Guides</h3>
42 * <p>For more information about communicating with USB hardware, read the
43 * <a href="{@docRoot}guide/topics/usb/index.html">USB</a> developer guide.</p>
44 * </div>
45 */
46public class UsbManager {
47    private static final String TAG = "UsbManager";
48
49   /**
50     * Broadcast Action:  A sticky broadcast for USB state change events when in device mode.
51     *
52     * This is a sticky broadcast for clients that includes USB connected/disconnected state,
53     * <ul>
54     * <li> {@link #USB_CONNECTED} boolean indicating whether USB is connected or disconnected.
55     * <li> {@link #USB_CONFIGURED} boolean indicating whether USB is configured.
56     * currently zero if not configured, one for configured.
57     * <li> {@link #USB_FUNCTION_MASS_STORAGE} boolean extra indicating whether the
58     * mass storage function is enabled
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     * </ul>
70     *
71     * {@hide}
72     */
73    public static final String ACTION_USB_STATE =
74            "android.hardware.usb.action.USB_STATE";
75
76   /**
77     * Broadcast Action:  A broadcast for USB device attached event.
78     *
79     * This intent is sent when a USB device is attached to the USB bus when in host mode.
80     * <ul>
81     * <li> {@link #EXTRA_DEVICE} containing the {@link android.hardware.usb.UsbDevice}
82     * for the attached device
83     * </ul>
84     */
85    public static final String ACTION_USB_DEVICE_ATTACHED =
86            "android.hardware.usb.action.USB_DEVICE_ATTACHED";
87
88   /**
89     * Broadcast Action:  A broadcast for USB device detached event.
90     *
91     * This intent is sent when a USB device is detached from the USB bus when in host mode.
92     * <ul>
93     * <li> {@link #EXTRA_DEVICE} containing the {@link android.hardware.usb.UsbDevice}
94     * for the detached device
95     * </ul>
96     */
97    public static final String ACTION_USB_DEVICE_DETACHED =
98            "android.hardware.usb.action.USB_DEVICE_DETACHED";
99
100   /**
101     * Broadcast Action:  A broadcast for USB accessory attached event.
102     *
103     * This intent is sent when a USB accessory is attached.
104     * <ul>
105     * <li> {@link #EXTRA_ACCESSORY} containing the {@link android.hardware.usb.UsbAccessory}
106     * for the attached accessory
107     * </ul>
108     */
109    public static final String ACTION_USB_ACCESSORY_ATTACHED =
110            "android.hardware.usb.action.USB_ACCESSORY_ATTACHED";
111
112   /**
113     * Broadcast Action:  A broadcast for USB accessory detached event.
114     *
115     * This intent is sent when a USB accessory is detached.
116     * <ul>
117     * <li> {@link #EXTRA_ACCESSORY} containing the {@link UsbAccessory}
118     * for the attached accessory that was detached
119     * </ul>
120     */
121    public static final String ACTION_USB_ACCESSORY_DETACHED =
122            "android.hardware.usb.action.USB_ACCESSORY_DETACHED";
123
124    /**
125     * Boolean extra indicating whether USB is connected or disconnected.
126     * Used in extras for the {@link #ACTION_USB_STATE} broadcast.
127     *
128     * {@hide}
129     */
130    public static final String USB_CONNECTED = "connected";
131
132    /**
133     * Boolean extra indicating whether USB is configured.
134     * Used in extras for the {@link #ACTION_USB_STATE} broadcast.
135     *
136     * {@hide}
137     */
138    public static final String USB_CONFIGURED = "configured";
139
140    /**
141     * Name of the USB mass storage USB function.
142     * Used in extras for the {@link #ACTION_USB_STATE} broadcast
143     *
144     * {@hide}
145     */
146    public static final String USB_FUNCTION_MASS_STORAGE = "mass_storage";
147
148    /**
149     * Name of the adb USB function.
150     * Used in extras for the {@link #ACTION_USB_STATE} broadcast
151     *
152     * {@hide}
153     */
154    public static final String USB_FUNCTION_ADB = "adb";
155
156    /**
157     * Name of the RNDIS ethernet USB function.
158     * Used in extras for the {@link #ACTION_USB_STATE} broadcast
159     *
160     * {@hide}
161     */
162    public static final String USB_FUNCTION_RNDIS = "rndis";
163
164    /**
165     * Name of the MTP USB function.
166     * Used in extras for the {@link #ACTION_USB_STATE} broadcast
167     *
168     * {@hide}
169     */
170    public static final String USB_FUNCTION_MTP = "mtp";
171
172    /**
173     * Name of the PTP USB function.
174     * Used in extras for the {@link #ACTION_USB_STATE} broadcast
175     *
176     * {@hide}
177     */
178    public static final String USB_FUNCTION_PTP = "ptp";
179
180    /**
181     * Name of the Accessory USB function.
182     * Used in extras for the {@link #ACTION_USB_STATE} broadcast
183     *
184     * {@hide}
185     */
186    public static final String USB_FUNCTION_ACCESSORY = "accessory";
187
188    /**
189     * Name of extra for {@link #ACTION_USB_DEVICE_ATTACHED} and
190     * {@link #ACTION_USB_DEVICE_DETACHED} broadcasts
191     * containing the UsbDevice object for the device.
192     */
193
194    public static final String EXTRA_DEVICE = "device";
195
196    /**
197     * Name of extra for {@link #ACTION_USB_ACCESSORY_ATTACHED} and
198     * {@link #ACTION_USB_ACCESSORY_DETACHED} broadcasts
199     * containing the UsbAccessory object for the accessory.
200     */
201    public static final String EXTRA_ACCESSORY = "accessory";
202
203    /**
204     * Name of extra added to the {@link android.app.PendingIntent}
205     * passed into {@link #requestPermission(UsbDevice, PendingIntent)}
206     * or {@link #requestPermission(UsbAccessory, PendingIntent)}
207     * containing a boolean value indicating whether the user granted permission or not.
208     */
209    public static final String EXTRA_PERMISSION_GRANTED = "permission";
210
211    private final Context mContext;
212    private final IUsbManager mService;
213
214    /**
215     * {@hide}
216     */
217    public UsbManager(Context context, IUsbManager service) {
218        mContext = context;
219        mService = service;
220    }
221
222    /**
223     * Returns a HashMap containing all USB devices currently attached.
224     * USB device name is the key for the returned HashMap.
225     * The result will be empty if no devices are attached, or if
226     * USB host mode is inactive or unsupported.
227     *
228     * @return HashMap containing all connected USB devices.
229     */
230    public HashMap<String,UsbDevice> getDeviceList() {
231        Bundle bundle = new Bundle();
232        try {
233            mService.getDeviceList(bundle);
234            HashMap<String,UsbDevice> result = new HashMap<String,UsbDevice>();
235            for (String name : bundle.keySet()) {
236                result.put(name, (UsbDevice)bundle.get(name));
237            }
238            return result;
239        } catch (RemoteException e) {
240            Log.e(TAG, "RemoteException in getDeviceList", e);
241            return null;
242        }
243    }
244
245    /**
246     * Opens the device so it can be used to send and receive
247     * data using {@link android.hardware.usb.UsbRequest}.
248     *
249     * @param device the device to open
250     * @return true if we successfully opened the device
251     */
252    public UsbDeviceConnection openDevice(UsbDevice device) {
253        try {
254            String deviceName = device.getDeviceName();
255            ParcelFileDescriptor pfd = mService.openDevice(deviceName);
256            if (pfd != null) {
257                UsbDeviceConnection connection = new UsbDeviceConnection(device);
258                boolean result = connection.open(deviceName, pfd);
259                pfd.close();
260                if (result) {
261                    return connection;
262                }
263            }
264        } catch (Exception e) {
265            Log.e(TAG, "exception in UsbManager.openDevice", e);
266        }
267        return null;
268    }
269
270    /**
271     * Returns a list of currently attached USB accessories.
272     * (in the current implementation there can be at most one)
273     *
274     * @return list of USB accessories, or null if none are attached.
275     */
276    public UsbAccessory[] getAccessoryList() {
277        try {
278            UsbAccessory accessory = mService.getCurrentAccessory();
279            if (accessory == null) {
280                return null;
281            } else {
282                return new UsbAccessory[] { accessory };
283            }
284        } catch (RemoteException e) {
285            Log.e(TAG, "RemoteException in getAccessoryList", e);
286            return null;
287        }
288    }
289
290    /**
291     * Opens a file descriptor for reading and writing data to the USB accessory.
292     *
293     * @param accessory the USB accessory to open
294     * @return file descriptor, or null if the accessor could not be opened.
295     */
296    public ParcelFileDescriptor openAccessory(UsbAccessory accessory) {
297        try {
298            return mService.openAccessory(accessory);
299        } catch (RemoteException e) {
300            Log.e(TAG, "RemoteException in openAccessory", e);
301            return null;
302        }
303    }
304
305    /**
306     * Returns true if the caller has permission to access the device.
307     * Permission might have been granted temporarily via
308     * {@link #requestPermission(UsbDevice, PendingIntent)} or
309     * by the user choosing the caller as the default application for the device.
310     *
311     * @param device to check permissions for
312     * @return true if caller has permission
313     */
314    public boolean hasPermission(UsbDevice device) {
315        try {
316            return mService.hasDevicePermission(device);
317        } catch (RemoteException e) {
318            Log.e(TAG, "RemoteException in hasPermission", e);
319            return false;
320        }
321    }
322
323    /**
324     * Returns true if the caller has permission to access the accessory.
325     * Permission might have been granted temporarily via
326     * {@link #requestPermission(UsbAccessory, PendingIntent)} or
327     * by the user choosing the caller as the default application for the accessory.
328     *
329     * @param accessory to check permissions for
330     * @return true if caller has permission
331     */
332    public boolean hasPermission(UsbAccessory accessory) {
333        try {
334            return mService.hasAccessoryPermission(accessory);
335        } catch (RemoteException e) {
336            Log.e(TAG, "RemoteException in hasPermission", e);
337            return false;
338        }
339    }
340
341    /**
342     * Requests temporary permission for the given package to access the device.
343     * This may result in a system dialog being displayed to the user
344     * if permission had not already been granted.
345     * Success or failure is returned via the {@link android.app.PendingIntent} pi.
346     * If successful, this grants the caller permission to access the device only
347     * until the device is disconnected.
348     *
349     * The following extras will be added to pi:
350     * <ul>
351     * <li> {@link #EXTRA_DEVICE} containing the device passed into this call
352     * <li> {@link #EXTRA_PERMISSION_GRANTED} containing boolean indicating whether
353     * permission was granted by the user
354     * </ul>
355     *
356     * @param device to request permissions for
357     * @param pi PendingIntent for returning result
358     */
359    public void requestPermission(UsbDevice device, PendingIntent pi) {
360        try {
361            mService.requestDevicePermission(device, mContext.getPackageName(), pi);
362        } catch (RemoteException e) {
363            Log.e(TAG, "RemoteException in requestPermission", e);
364        }
365    }
366
367    /**
368     * Requests temporary permission for the given package to access the accessory.
369     * This may result in a system dialog being displayed to the user
370     * if permission had not already been granted.
371     * Success or failure is returned via the {@link android.app.PendingIntent} pi.
372     * If successful, this grants the caller permission to access the accessory only
373     * until the device is disconnected.
374     *
375     * The following extras will be added to pi:
376     * <ul>
377     * <li> {@link #EXTRA_ACCESSORY} containing the accessory passed into this call
378     * <li> {@link #EXTRA_PERMISSION_GRANTED} containing boolean indicating whether
379     * permission was granted by the user
380     * </ul>
381     *
382     * @param accessory to request permissions for
383     * @param pi PendingIntent for returning result
384     */
385    public void requestPermission(UsbAccessory accessory, PendingIntent pi) {
386        try {
387            mService.requestAccessoryPermission(accessory, mContext.getPackageName(), pi);
388        } catch (RemoteException e) {
389            Log.e(TAG, "RemoteException in requestPermission", e);
390        }
391    }
392
393    private static boolean propertyContainsFunction(String property, String function) {
394        String functions = SystemProperties.get(property, "");
395        int index = functions.indexOf(function);
396        if (index < 0) return false;
397        if (index > 0 && functions.charAt(index - 1) != ',') return false;
398        int charAfter = index + function.length();
399        if (charAfter < functions.length() && functions.charAt(charAfter) != ',') return false;
400        return true;
401    }
402
403    /**
404     * Returns true if the specified USB function is currently enabled.
405     *
406     * @param function name of the USB function
407     * @return true if the USB function is enabled.
408     *
409     * {@hide}
410     */
411    public boolean isFunctionEnabled(String function) {
412        return propertyContainsFunction("sys.usb.config", function);
413    }
414
415    /**
416     * Returns the current default USB function.
417     *
418     * @return name of the default function.
419     *
420     * {@hide}
421     */
422    public String getDefaultFunction() {
423        String functions = SystemProperties.get("persist.sys.usb.config", "");
424        int commaIndex = functions.indexOf(',');
425        if (commaIndex > 0) {
426            return functions.substring(0, commaIndex);
427        } else {
428            return functions;
429        }
430    }
431
432    /**
433     * Sets the current USB function.
434     * If function is null, then the current function is set to the default function.
435     *
436     * @param function name of the USB function, or null to restore the default function
437     * @param makeDefault true if the function should be set as the new default function
438     *
439     * {@hide}
440     */
441    public void setCurrentFunction(String function, boolean makeDefault) {
442        try {
443            mService.setCurrentFunction(function, makeDefault);
444        } catch (RemoteException e) {
445            Log.e(TAG, "RemoteException in setCurrentFunction", e);
446        }
447    }
448
449    /**
450     * Sets the file path for USB mass storage backing file.
451     *
452     * @param path backing file path
453     *
454     * {@hide}
455     */
456    public void setMassStorageBackingFile(String path) {
457        try {
458            mService.setMassStorageBackingFile(path);
459        } catch (RemoteException e) {
460            Log.e(TAG, "RemoteException in setDefaultFunction", e);
461        }
462    }
463}
464