content: /*
   * Copyright (C) 2010 The Android Open Source Project
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  
  package android.hardware.usb;
  
  import android.Manifest;
  import android.annotation.NonNull;
  import android.annotation.Nullable;
  import android.annotation.RequiresFeature;
  import android.annotation.RequiresPermission;
  import android.annotation.SdkConstant;
  import android.annotation.SdkConstant.SdkConstantType;
  import android.annotation.SystemApi;
  import android.annotation.SystemService;
  import android.annotation.UnsupportedAppUsage;
  import android.app.PendingIntent;
  import android.content.ComponentName;
  import android.content.Context;
  import android.content.pm.PackageManager;
  import android.content.pm.PackageManager.NameNotFoundException;
  import android.hardware.usb.gadget.V1_0.GadgetFunction;
  import android.os.Build;
  import android.os.Bundle;
  import android.os.ParcelFileDescriptor;
  import android.os.Process;
  import android.os.RemoteException;
  import android.util.Log;
  
  import java.util.ArrayList;
  import java.util.Collections;
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  import java.util.StringJoiner;
  
  /**
   * This class allows you to access the state of USB and communicate with USB devices.
   * Currently only host mode is supported in the public API.
   *
   * <div class="special reference">
   * <h3>Developer Guides</h3>
   * <p>For more information about communicating with USB hardware, read the
   * <a href="{@docRoot}guide/topics/connectivity/usb/index.html">USB developer guide</a>.</p>
   * </div>
   */
  @SystemService(Context.USB_SERVICE)
  public class UsbManager {
      private static final String TAG = "UsbManager";
  
     /**
       * Broadcast Action:  A sticky broadcast for USB state change events when in device mode.
       *
       * This is a sticky broadcast for clients that includes USB connected/disconnected state,
       * <ul>
       * <li> {@link #USB_CONNECTED} boolean indicating whether USB is connected or disconnected.
       * <li> {@link #USB_HOST_CONNECTED} boolean indicating whether USB is connected or
       *     disconnected as host.
       * <li> {@link #USB_CONFIGURED} boolean indicating whether USB is configured.
       * currently zero if not configured, one for configured.
       * <li> {@link #USB_FUNCTION_ADB} boolean extra indicating whether the
       * adb function is enabled
       * <li> {@link #USB_FUNCTION_RNDIS} boolean extra indicating whether the
       * RNDIS ethernet function is enabled
       * <li> {@link #USB_FUNCTION_MTP} boolean extra indicating whether the
       * MTP function is enabled
       * <li> {@link #USB_FUNCTION_PTP} boolean extra indicating whether the
       * PTP function is enabled
       * <li> {@link #USB_FUNCTION_ACCESSORY} boolean extra indicating whether the
       * accessory function is enabled
       * <li> {@link #USB_FUNCTION_AUDIO_SOURCE} boolean extra indicating whether the
       * audio source function is enabled
       * <li> {@link #USB_FUNCTION_MIDI} boolean extra indicating whether the
       * MIDI function is enabled
       * </ul>
       * If the sticky intent has not been found, that indicates USB is disconnected,
       * USB is not configued, MTP function is enabled, and all the other functions are disabled.
       *
       * {@hide}
       */
      @UnsupportedAppUsage
      public static final String ACTION_USB_STATE =
              "android.hardware.usb.action.USB_STATE";
  
      /**
       * Broadcast Action: A broadcast for USB port changes.
       *
       * This intent is sent when a USB port is added, removed, or changes state.
       *
       * @hide
       */
      @SystemApi
      @RequiresPermission(Manifest.permission.MANAGE_USB)
      public static final String ACTION_USB_PORT_CHANGED =
              "android.hardware.usb.action.USB_PORT_CHANGED";
  
     /**
       * Activity intent sent when user attaches a USB device.
       *
       * This intent is sent when a USB device is attached to the USB bus when in host mode.
       * <ul>
       * <li> {@link #EXTRA_DEVICE} containing the {@link android.hardware.usb.UsbDevice}
       * for the attached device
       * </ul>
       */
      @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
      public static final String ACTION_USB_DEVICE_ATTACHED =
              "android.hardware.usb.action.USB_DEVICE_ATTACHED";
  
     /**
       * Broadcast Action:  A broadcast for USB device detached event.
       *
       * This intent is sent when a USB device is detached from the USB bus when in host mode.
       * <ul>
       * <li> {@link #EXTRA_DEVICE} containing the {@link android.hardware.usb.UsbDevice}
       * for the detached device
       * </ul>
       */
      @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
      public static final String ACTION_USB_DEVICE_DETACHED =
              "android.hardware.usb.action.USB_DEVICE_DETACHED";
  
     /**
       * Activity intent sent when user attaches a USB accessory.
       *
       * <ul>
       * <li> {@link #EXTRA_ACCESSORY} containing the {@link android.hardware.usb.UsbAccessory}
       * for the attached accessory
       * </ul>
       */
      @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
      public static final String ACTION_USB_ACCESSORY_ATTACHED =
              "android.hardware.usb.action.USB_ACCESSORY_ATTACHED";
  
     /**
       * Broadcast Action:  A broadcast for USB accessory detached event.
       *
       * This intent is sent when a USB accessory is detached.
       * <ul>
       * <li> {@link #EXTRA_ACCESSORY} containing the {@link UsbAccessory}
       * for the attached accessory that was detached
       * </ul>
       */
      @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
      public static final String ACTION_USB_ACCESSORY_DETACHED =
              "android.hardware.usb.action.USB_ACCESSORY_DETACHED";
  
      /**
       * Boolean extra indicating whether USB is connected or disconnected.
       * Used in extras for the {@link #ACTION_USB_STATE} broadcast.
       *
       * {@hide}
       */
      @UnsupportedAppUsage
      public static final String USB_CONNECTED = "connected";
  
      /**
       * Boolean extra indicating whether USB is connected or disconnected as host.
       * Used in extras for the {@link #ACTION_USB_STATE} broadcast.
       *
       * {@hide}
       */
      public static final String USB_HOST_CONNECTED = "host_connected";
  
      /**
       * Boolean extra indicating whether USB is configured.
       * Used in extras for the {@link #ACTION_USB_STATE} broadcast.
       *
       * {@hide}
       */
      public static final String USB_CONFIGURED = "configured";
  
      /**
       * Boolean extra indicating whether confidential user data, such as photos, should be
       * made available on the USB connection. This variable will only be set when the user
       * has explicitly asked for this data to be unlocked.
       * Used in extras for the {@link #ACTION_USB_STATE} broadcast.
       *
       * {@hide}
       */
      @UnsupportedAppUsage
      public static final String USB_DATA_UNLOCKED = "unlocked";
  
      /**
       * A placeholder indicating that no USB function is being specified.
       * Used for compatibility with old init scripts to indicate no functions vs. charging function.
       *
       * {@hide}
       */
      @UnsupportedAppUsage
      public static final String USB_FUNCTION_NONE = "none";
  
      /**
       * Name of the adb USB function.
       * Used in extras for the {@link #ACTION_USB_STATE} broadcast
       *
       * {@hide}
       */
      public static final String USB_FUNCTION_ADB = "adb";
  
      /**
       * Name of the RNDIS ethernet USB function.
       * Used in extras for the {@link #ACTION_USB_STATE} broadcast
       *
       * {@hide}
       */
      public static final String USB_FUNCTION_RNDIS = "rndis";
  
      /**
       * Name of the MTP USB function.
       * Used in extras for the {@link #ACTION_USB_STATE} broadcast
       *
       * {@hide}
       */
      public static final String USB_FUNCTION_MTP = "mtp";
  
      /**
       * Name of the PTP USB function.
       * Used in extras for the {@link #ACTION_USB_STATE} broadcast
       *
       * {@hide}
       */
      public static final String USB_FUNCTION_PTP = "ptp";
  
      /**
       * Name of the audio source USB function.
       * Used in extras for the {@link #ACTION_USB_STATE} broadcast
       *
       * {@hide}
       */
      public static final String USB_FUNCTION_AUDIO_SOURCE = "audio_source";
  
      /**
       * Name of the MIDI USB function.
       * Used in extras for the {@link #ACTION_USB_STATE} broadcast
       *
       * {@hide}
       */
      public static final String USB_FUNCTION_MIDI = "midi";
  
      /**
       * Name of the Accessory USB function.
       * Used in extras for the {@link #ACTION_USB_STATE} broadcast
       *
       * {@hide}
       */
      public static final String USB_FUNCTION_ACCESSORY = "accessory";
  
      /**
       * Name of extra for {@link #ACTION_USB_PORT_CHANGED}
       * containing the {@link UsbPort} object for the port.
       *
       * @hide
       */
      public static final String EXTRA_PORT = "port";
  
      /**
       * Name of extra for {@link #ACTION_USB_PORT_CHANGED}
       * containing the {@link UsbPortStatus} object for the port, or null if the port
       * was removed.
       *
       * @hide
       */
      public static final String EXTRA_PORT_STATUS = "portStatus";
  
      /**
       * Name of extra for {@link #ACTION_USB_DEVICE_ATTACHED} and
       * {@link #ACTION_USB_DEVICE_DETACHED} broadcasts
       * containing the {@link UsbDevice} object for the device.
       */
      public static final String EXTRA_DEVICE = "device";
  
      /**
       * Name of extra for {@link #ACTION_USB_ACCESSORY_ATTACHED} and
       * {@link #ACTION_USB_ACCESSORY_DETACHED} broadcasts
       * containing the {@link UsbAccessory} object for the accessory.
       */
      public static final String EXTRA_ACCESSORY = "accessory";
  
      /**
       * Name of extra added to the {@link android.app.PendingIntent}
       * passed into {@link #requestPermission(UsbDevice, PendingIntent)}
       * or {@link #requestPermission(UsbAccessory, PendingIntent)}
       * containing a boolean value indicating whether the user granted permission or not.
       */
      public static final String EXTRA_PERMISSION_GRANTED = "permission";
  
      /**
       * Name of extra added to start systemui.usb.UsbPermissionActivity
       * containing package name of the app which requests USB permission.
       *
       * @hide
       */
      public static final String EXTRA_PACKAGE = "android.hardware.usb.extra.PACKAGE";
  
      /**
       * Name of extra added to start systemui.usb.UsbPermissionActivity
       * containing the whether the app which requests USB permission can be set as default handler
       * for USB device attach event or USB accessory attach event or not.
       *
       * @hide
       */
      public static final String EXTRA_CAN_BE_DEFAULT = "android.hardware.usb.extra.CAN_BE_DEFAULT";
  
      /**
       * Code for the charging usb function. Passed into {@link #setCurrentFunctions(long)}
       * {@hide}
       */
      public static final long FUNCTION_NONE = 0;
  
      /**
       * Code for the mtp usb function. Passed as a mask into {@link #setCurrentFunctions(long)}
       * {@hide}
       */
      public static final long FUNCTION_MTP = GadgetFunction.MTP;
  
      /**
       * Code for the ptp usb function. Passed as a mask into {@link #setCurrentFunctions(long)}
       * {@hide}
       */
      public static final long FUNCTION_PTP = GadgetFunction.PTP;
  
      /**
       * Code for the rndis usb function. Passed as a mask into {@link #setCurrentFunctions(long)}
       * {@hide}
       */
      public static final long FUNCTION_RNDIS = GadgetFunction.RNDIS;
  
      /**
       * Code for the midi usb function. Passed as a mask into {@link #setCurrentFunctions(long)}
       * {@hide}
       */
      public static final long FUNCTION_MIDI = GadgetFunction.MIDI;
  
      /**
       * Code for the accessory usb function.
       * {@hide}
       */
      public static final long FUNCTION_ACCESSORY = GadgetFunction.ACCESSORY;
  
      /**
       * Code for the audio source usb function.
       * {@hide}
       */
      public static final long FUNCTION_AUDIO_SOURCE = GadgetFunction.AUDIO_SOURCE;
  
      /**
       * Code for the adb usb function.
       * {@hide}
       */
      public static final long FUNCTION_ADB = GadgetFunction.ADB;
  
      private static final long SETTABLE_FUNCTIONS = FUNCTION_MTP | FUNCTION_PTP | FUNCTION_RNDIS
              | FUNCTION_MIDI;
  
      private static final Map<String, Long> FUNCTION_NAME_TO_CODE = new HashMap<>();
  
      static {
          FUNCTION_NAME_TO_CODE.put(UsbManager.USB_FUNCTION_MTP, FUNCTION_MTP);
          FUNCTION_NAME_TO_CODE.put(UsbManager.USB_FUNCTION_PTP, FUNCTION_PTP);
          FUNCTION_NAME_TO_CODE.put(UsbManager.USB_FUNCTION_RNDIS, FUNCTION_RNDIS);
          FUNCTION_NAME_TO_CODE.put(UsbManager.USB_FUNCTION_MIDI, FUNCTION_MIDI);
          FUNCTION_NAME_TO_CODE.put(UsbManager.USB_FUNCTION_ACCESSORY, FUNCTION_ACCESSORY);
          FUNCTION_NAME_TO_CODE.put(UsbManager.USB_FUNCTION_AUDIO_SOURCE, FUNCTION_AUDIO_SOURCE);
          FUNCTION_NAME_TO_CODE.put(UsbManager.USB_FUNCTION_ADB, FUNCTION_ADB);
      }
  
      private final Context mContext;
      private final IUsbManager mService;
  
      /**
       * {@hide}
       */
      @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
      public UsbManager(Context context, IUsbManager service) {
          mContext = context;
          mService = service;
      }
  
      /**
       * Returns a HashMap containing all USB devices currently attached.
       * USB device name is the key for the returned HashMap.
       * The result will be empty if no devices are attached, or if
       * USB host mode is inactive or unsupported.
       *
       * @return HashMap containing all connected USB devices.
       */
      @RequiresFeature(PackageManager.FEATURE_USB_HOST)
      public HashMap<String,UsbDevice> getDeviceList() {
          HashMap<String,UsbDevice> result = new HashMap<String,UsbDevice>();
          if (mService == null) {
              return result;
          }
          Bundle bundle = new Bundle();
          try {
              mService.getDeviceList(bundle);
              for (String name : bundle.keySet()) {
                  result.put(name, (UsbDevice)bundle.get(name));
              }
              return result;
          } catch (RemoteException e) {
              throw e.rethrowFromSystemServer();
          }
      }
  
      /**
       * Opens the device so it can be used to send and receive
       * data using {@link android.hardware.usb.UsbRequest}.
       *
       * @param device the device to open
       * @return a {@link UsbDeviceConnection}, or {@code null} if open failed
       */
      @RequiresFeature(PackageManager.FEATURE_USB_HOST)
      public UsbDeviceConnection openDevice(UsbDevice device) {
          try {
              String deviceName = device.getDeviceName();
              ParcelFileDescriptor pfd = mService.openDevice(deviceName, mContext.getPackageName());
              if (pfd != null) {
                  UsbDeviceConnection connection = new UsbDeviceConnection(device);
                  boolean result = connection.open(deviceName, pfd, mContext);
                  pfd.close();
                  if (result) {
                      return connection;
                  }
              }
          } catch (Exception e) {
              Log.e(TAG, "exception in UsbManager.openDevice", e);
          }
          return null;
      }
  
      /**
       * Returns a list of currently attached USB accessories.
       * (in the current implementation there can be at most one)
       *
       * @return list of USB accessories, or null if none are attached.
       */
      @RequiresFeature(PackageManager.FEATURE_USB_ACCESSORY)
      public UsbAccessory[] getAccessoryList() {
          if (mService == null) {
              return null;
          }
          try {
              UsbAccessory accessory = mService.getCurrentAccessory();
              if (accessory == null) {
                  return null;
              } else {
                  return new UsbAccessory[] { accessory };
              }
          } catch (RemoteException e) {
              throw e.rethrowFromSystemServer();
          }
      }
  
      /**
       * Opens a file descriptor for reading and writing data to the USB accessory.
       *
       * <p>If data is read from the {@link java.io.InputStream} created from this file descriptor all
       * data of a USB transfer should be read at once. If only a partial request is read the rest of
       * the transfer is dropped.
       *
       * @param accessory the USB accessory to open
       * @return file descriptor, or null if the accessory could not be opened.
       */
      @RequiresFeature(PackageManager.FEATURE_USB_ACCESSORY)
      public ParcelFileDescriptor openAccessory(UsbAccessory accessory) {
          try {
              return mService.openAccessory(accessory);
          } catch (RemoteException e) {
              throw e.rethrowFromSystemServer();
          }
      }
  
      /**
       * Gets the functionfs control file descriptor for the given function, with
       * the usb descriptors and strings already written. The file descriptor is used
       * by the function implementation to handle events and control requests.
       *
       * @param function to get control fd for. Currently {@link #FUNCTION_MTP} and
       * {@link #FUNCTION_PTP} are supported.
       * @return A ParcelFileDescriptor holding the valid fd, or null if the fd was not found.
       *
       * {@hide}
       */
      public ParcelFileDescriptor getControlFd(long function) {
          try {
              return mService.getControlFd(function);
          } catch (RemoteException e) {
              throw e.rethrowFromSystemServer();
          }
      }
  
      /**
       * Returns true if the caller has permission to access the device.
       * Permission might have been granted temporarily via
       * {@link #requestPermission(UsbDevice, PendingIntent)} or
       * by the user choosing the caller as the default application for the device.
       * Permission for USB devices of class {@link UsbConstants#USB_CLASS_VIDEO} for clients that
       * target SDK {@link android.os.Build.VERSION_CODES#P} and above can be granted only if they
       * have additionally the {@link android.Manifest.permission#CAMERA} permission.
       *
       * @param device to check permissions for
       * @return true if caller has permission
       */
      @RequiresFeature(PackageManager.FEATURE_USB_HOST)
      public boolean hasPermission(UsbDevice device) {
          if (mService == null) {
              return false;
          }
          try {
              return mService.hasDevicePermission(device, mContext.getPackageName());
          } catch (RemoteException e) {
              throw e.rethrowFromSystemServer();
          }
      }
  
      /**
       * Returns true if the caller has permission to access the accessory.
       * Permission might have been granted temporarily via
       * {@link #requestPermission(UsbAccessory, PendingIntent)} or
       * by the user choosing the caller as the default application for the accessory.
       *
       * @param accessory to check permissions for
       * @return true if caller has permission
       */
      @RequiresFeature(PackageManager.FEATURE_USB_ACCESSORY)
      public boolean hasPermission(UsbAccessory accessory) {
          if (mService == null) {
              return false;
          }
          try {
              return mService.hasAccessoryPermission(accessory);
          } catch (RemoteException e) {
              throw e.rethrowFromSystemServer();
          }
      }
  
      /**
       * Requests temporary permission for the given package to access the device.
       * This may result in a system dialog being displayed to the user
       * if permission had not already been granted.
       * Success or failure is returned via the {@link android.app.PendingIntent} pi.
       * If successful, this grants the caller permission to access the device only
       * until the device is disconnected.
       *
       * The following extras will be added to pi:
       * <ul>
       * <li> {@link #EXTRA_DEVICE} containing the device passed into this call
       * <li> {@link #EXTRA_PERMISSION_GRANTED} containing boolean indicating whether
       * permission was granted by the user
       * </ul>
       *
       * Permission for USB devices of class {@link UsbConstants#USB_CLASS_VIDEO} for clients that
       * target SDK {@link android.os.Build.VERSION_CODES#P} and above can be granted only if they
       * have additionally the {@link android.Manifest.permission#CAMERA} permission.
       *
       * @param device to request permissions for
       * @param pi PendingIntent for returning result
       */
      @RequiresFeature(PackageManager.FEATURE_USB_HOST)
      public void requestPermission(UsbDevice device, PendingIntent pi) {
          try {
              mService.requestDevicePermission(device, mContext.getPackageName(), pi);
          } catch (RemoteException e) {
              throw e.rethrowFromSystemServer();
          }
      }
  
      /**
       * Requests temporary permission for the given package to access the accessory.
       * This may result in a system dialog being displayed to the user
       * if permission had not already been granted.
       * Success or failure is returned via the {@link android.app.PendingIntent} pi.
       * If successful, this grants the caller permission to access the accessory only
       * until the device is disconnected.
       *
       * The following extras will be added to pi:
       * <ul>
       * <li> {@link #EXTRA_ACCESSORY} containing the accessory passed into this call
       * <li> {@link #EXTRA_PERMISSION_GRANTED} containing boolean indicating whether
       * permission was granted by the user
       * </ul>
       *
       * @param accessory to request permissions for
       * @param pi PendingIntent for returning result
       */
      @RequiresFeature(PackageManager.FEATURE_USB_ACCESSORY)
      public void requestPermission(UsbAccessory accessory, PendingIntent pi) {
          try {
              mService.requestAccessoryPermission(accessory, mContext.getPackageName(), pi);
          } catch (RemoteException e) {
              throw e.rethrowFromSystemServer();
          }
      }
  
      /**
       * Grants permission for USB device without showing system dialog.
       * Only system components can call this function.
       * @param device to request permissions for
       *
       * {@hide}
       */
      public void grantPermission(UsbDevice device) {
          grantPermission(device, Process.myUid());
      }
  
      /**
       * Grants permission for USB device to given uid without showing system dialog.
       * Only system components can call this function.
       * @param device to request permissions for
       * @uid uid to give permission
       *
       * {@hide}
       */
      public void grantPermission(UsbDevice device, int uid) {
          try {
              mService.grantDevicePermission(device, uid);
          } catch (RemoteException e) {
              throw e.rethrowFromSystemServer();
          }
      }
  
      /**
       * Grants permission to specified package for USB device without showing system dialog.
       * Only system components can call this function, as it requires the MANAGE_USB permission.
       * @param device to request permissions for
       * @param packageName of package to grant permissions
       *
       * {@hide}
       */
      @SystemApi
      @RequiresPermission(Manifest.permission.MANAGE_USB)
      public void grantPermission(UsbDevice device, String packageName) {
          try {
              int uid = mContext.getPackageManager()
                  .getPackageUidAsUser(packageName, mContext.getUserId());
              grantPermission(device, uid);
          } catch (NameNotFoundException e) {
              Log.e(TAG, "Package " + packageName + " not found.", e);
          }
      }
  
      /**
       * Returns true if the specified USB function is currently enabled when in device mode.
       * <p>
       * USB functions represent interfaces which are published to the host to access
       * services offered by the device.
       * </p>
       *
       * @deprecated use getCurrentFunctions() instead.
       * @param function name of the USB function
       * @return true if the USB function is enabled
       *
       * {@hide}
       */
      @Deprecated
      @UnsupportedAppUsage
      public boolean isFunctionEnabled(String function) {
          try {
              return mService.isFunctionEnabled(function);
          } catch (RemoteException e) {
              throw e.rethrowFromSystemServer();
          }
      }
  
      /**
       * Sets the current USB functions when in device mode.
       * <p>
       * USB functions represent interfaces which are published to the host to access
       * services offered by the device.
       * </p><p>
       * This method is intended to select among primary USB functions.  The system may
       * automatically activate additional functions such as {@link #USB_FUNCTION_ADB}
       * or {@link #USB_FUNCTION_ACCESSORY} based on other settings and states.
       * </p><p>
       * An argument of 0 indicates that the device is charging, and can pick any
       * appropriate function for that purpose.
       * </p><p>
       * Note: This function is asynchronous and may fail silently without applying
       * the requested changes.
       * </p>
       *
       * @param functions the USB function(s) to set, as a bitwise mask.
       *                  Must satisfy {@link UsbManager#areSettableFunctions}
       *
       * {@hide}
       */
      public void setCurrentFunctions(long functions) {
          try {
              mService.setCurrentFunctions(functions);
          } catch (RemoteException e) {
              throw e.rethrowFromSystemServer();
          }
      }
  
      /**
       * Sets the current USB functions when in device mode.
       *
       * @deprecated use setCurrentFunctions(long) instead.
       * @param functions the USB function(s) to set.
       * @param usbDataUnlocked unused
  
       * {@hide}
       */
      @Deprecated
      @UnsupportedAppUsage
      public void setCurrentFunction(String functions, boolean usbDataUnlocked) {
          try {
              mService.setCurrentFunction(functions, usbDataUnlocked);
          } catch (RemoteException e) {
              throw e.rethrowFromSystemServer();
          }
      }
  
      /**
       * Returns the current USB functions in device mode.
       * <p>
       * This function returns the state of primary USB functions and can return a
       * mask containing any usb function(s) except for ADB.
       * </p>
       *
       * @return The currently enabled functions, in a bitwise mask.
       * A zero mask indicates that the current function is the charging function.
       *
       * {@hide}
       */
      public long getCurrentFunctions() {
          try {
              return mService.getCurrentFunctions();
          } catch (RemoteException e) {
              throw e.rethrowFromSystemServer();
          }
      }
  
      /**
       * Sets the screen unlocked functions, which are persisted and set as the current functions
       * whenever the screen is unlocked.
       * <p>
       * A zero mask has the effect of switching off this feature, so functions
       * no longer change on screen unlock.
       * </p><p>
       * Note: When the screen is on, this method will apply given functions as current functions,
       * which is asynchronous and may fail silently without applying the requested changes.
       * </p>
       *
       * @param functions functions to set, in a bitwise mask.
       *                  Must satisfy {@link UsbManager#areSettableFunctions}
       *
       * {@hide}
       */
      public void setScreenUnlockedFunctions(long functions) {
          try {
              mService.setScreenUnlockedFunctions(functions);
          } catch (RemoteException e) {
              throw e.rethrowFromSystemServer();
          }
      }
  
      /**
       * Gets the current screen unlocked functions.
       *
       * @return The currently set screen enabled functions.
       * A zero mask indicates that the screen unlocked functions feature is not enabled.
       *
       * {@hide}
       */
      public long getScreenUnlockedFunctions() {
          try {
              return mService.getScreenUnlockedFunctions();
          } catch (RemoteException e) {
              throw e.rethrowFromSystemServer();
          }
      }
  
      /**
       * Returns a list of physical USB ports on the device.
       * <p>
       * This list is guaranteed to contain all dual-role USB Type C ports but it might
       * be missing other ports depending on whether the kernel USB drivers have been
       * updated to publish all of the device's ports through the new "dual_role_usb"
       * device class (which supports all types of ports despite its name).
       * </p>
       *
       * @return The list of USB ports
       *
       * @hide
       */
      @SystemApi
      @RequiresPermission(Manifest.permission.MANAGE_USB)
      public @NonNull List<UsbPort> getPorts() {
          if (mService == null) {
              return Collections.emptyList();
          }
  
          List<ParcelableUsbPort> parcelablePorts;
          try {
              parcelablePorts = mService.getPorts();
          } catch (RemoteException e) {
              throw e.rethrowFromSystemServer();
          }
  
          if (parcelablePorts == null) {
              return Collections.emptyList();
          } else {
              int numPorts = parcelablePorts.size();
  
              ArrayList<UsbPort> ports = new ArrayList<>(numPorts);
              for (int i = 0; i < numPorts; i++) {
                  ports.add(parcelablePorts.get(i).getUsbPort(this));
              }
  
              return ports;
          }
      }
  
      /**
       * Should only be called by {@link UsbPort#getStatus}.
       *
       * @hide
       */
      UsbPortStatus getPortStatus(UsbPort port) {
          try {
              return mService.getPortStatus(port.getId());
          } catch (RemoteException e) {
              throw e.rethrowFromSystemServer();
          }
      }
  
      /**
       * Should only be called by {@link UsbPort#setRoles}.
       *
       * @hide
       */
      void setPortRoles(UsbPort port, int powerRole, int dataRole) {
          Log.d(TAG, "setPortRoles Package:" + mContext.getPackageName());
          try {
              mService.setPortRoles(port.getId(), powerRole, dataRole);
          } catch (RemoteException e) {
              throw e.rethrowFromSystemServer();
          }
      }
  
      /**
       * Enables USB port contaminant detection algorithm.
       *
       * @hide
       */
      @RequiresPermission(Manifest.permission.MANAGE_USB)
      void enableContaminantDetection(@NonNull UsbPort port, boolean enable) {
          try {
              mService.enableContaminantDetection(port.getId(), enable);
          } catch (RemoteException e) {
              throw e.rethrowFromSystemServer();
          }
      }
  
      /**
       * Sets the component that will handle USB device connection.
       * <p>
       * Setting component allows to specify external USB host manager to handle use cases, where
       * selection dialog for an activity that will handle USB device is undesirable.
       * Only system components can call this function, as it requires the MANAGE_USB permission.
       *
       * @param usbDeviceConnectionHandler The component to handle usb connections,
       * {@code null} to unset.
       *
       * {@hide}
       */
      public void setUsbDeviceConnectionHandler(@Nullable ComponentName usbDeviceConnectionHandler) {
          try {
              mService.setUsbDeviceConnectionHandler(usbDeviceConnectionHandler);
          } catch (RemoteException e) {
              throw e.rethrowFromSystemServer();
          }
      }
  
      /**
       * Returns whether the given functions are valid inputs to UsbManager.
       * Currently the empty functions or any of MTP, PTP, RNDIS, MIDI are accepted.
       *
       * @return Whether the mask is settable.
       *
       * {@hide}
       */
      public static boolean areSettableFunctions(long functions) {
          return functions == FUNCTION_NONE
                  || ((~SETTABLE_FUNCTIONS & functions) == 0 && Long.bitCount(functions) == 1);
      }
  
      /**
       * Converts the given function mask to string. Maintains ordering with respect to init scripts.
       *
       * @return String representation of given mask
       *
       * {@hide}
       */
      public static String usbFunctionsToString(long functions) {
          StringJoiner joiner = new StringJoiner(",");
          if ((functions & FUNCTION_MTP) != 0) {
              joiner.add(UsbManager.USB_FUNCTION_MTP);
          }
          if ((functions & FUNCTION_PTP) != 0) {
              joiner.add(UsbManager.USB_FUNCTION_PTP);
          }
          if ((functions & FUNCTION_RNDIS) != 0) {
              joiner.add(UsbManager.USB_FUNCTION_RNDIS);
          }
          if ((functions & FUNCTION_MIDI) != 0) {
              joiner.add(UsbManager.USB_FUNCTION_MIDI);
          }
          if ((functions & FUNCTION_ACCESSORY) != 0) {
              joiner.add(UsbManager.USB_FUNCTION_ACCESSORY);
          }
          if ((functions & FUNCTION_AUDIO_SOURCE) != 0) {
              joiner.add(UsbManager.USB_FUNCTION_AUDIO_SOURCE);
          }
          if ((functions & FUNCTION_ADB) != 0) {
              joiner.add(UsbManager.USB_FUNCTION_ADB);
          }
          return joiner.toString();
      }
  
      /**
       * Parses a string of usb functions that are comma separated.
       *
       * @return A mask of all valid functions in the string
       *
       * {@hide}
       */
      public static long usbFunctionsFromString(String functions) {
          if (functions == null || functions.equals(USB_FUNCTION_NONE)) {
              return FUNCTION_NONE;
          }
          long ret = 0;
          for (String function : functions.split(",")) {
              if (FUNCTION_NAME_TO_CODE.containsKey(function)) {
                  ret |= FUNCTION_NAME_TO_CODE.get(function);
              } else if (function.length() > 0) {
                  throw new IllegalArgumentException("Invalid usb function " + functions);
              }
          }
          return ret;
      }
  }
  