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