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