All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.robolectric.shadows.ShadowUsbManager Maven / Gradle / Ivy

The newest version!
package org.robolectric.shadows;

import static android.os.Build.VERSION_CODES.M;
import static android.os.Build.VERSION_CODES.N;
import static android.os.Build.VERSION_CODES.N_MR1;
import static android.os.Build.VERSION_CODES.P;
import static android.os.Build.VERSION_CODES.Q;
import static org.robolectric.util.ReflectionHelpers.ClassParameter.from;
import static org.robolectric.util.ReflectionHelpers.callConstructor;
import static org.robolectric.util.ReflectionHelpers.getStaticField;

import android.annotation.Nullable;
import android.annotation.TargetApi;
import android.content.Intent;
import android.hardware.usb.UsbAccessory;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbManager;
import android.hardware.usb.UsbPort;
import android.hardware.usb.UsbPortStatus;
import android.os.ParcelFileDescriptor;
import com.google.common.base.Preconditions;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.ClassName;
import org.robolectric.annotation.HiddenApi;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.RealObject;
import org.robolectric.annotation.Resetter;
import org.robolectric.util.reflector.ForType;

/** Robolectric implementation of {@link android.hardware.usb.UsbManager}. */
@Implements(value = UsbManager.class)
public class ShadowUsbManager {

  @RealObject private UsbManager realUsbManager;

  /**
   * A mapping from the package names to a list of USB devices for which permissions are granted.
   */
  private static final HashMap> grantedDevicePermissions = new HashMap<>();

  /**
   * A mapping from the package names to a list of USB accessories for which permissions are
   * granted.
   */
  private static final HashMap> grantedAccessoryPermissions =
      new HashMap<>();

  /**
   * A mapping from the USB device names to the USB device instances.
   *
   * @see UsbManager#getDeviceList()
   */
  private static final HashMap usbDevices = new HashMap<>();

  /** A mapping from USB port ID to the port object. */
  private static final HashMap usbPorts = new HashMap<>();

  /** A mapping from USB port to the status of that port. */
  private static final HashMap usbPortStatuses = new HashMap<>();

  private static UsbAccessory attachedUsbAccessory = null;

  @Resetter
  public static void reset() {
    grantedDevicePermissions.clear();
    grantedAccessoryPermissions.clear();
    usbDevices.clear();
    usbPorts.clear();
    usbPortStatuses.clear();
    attachedUsbAccessory = null;
  }

  /** Returns true if the caller has permission to access the device. */
  @Implementation
  protected boolean hasPermission(UsbDevice device) {
    return hasPermissionForPackage(device, RuntimeEnvironment.getApplication().getPackageName());
  }

  /** Returns true if the given package has permission to access the device. */
  public boolean hasPermissionForPackage(UsbDevice device, String packageName) {
    List usbDevices = grantedDevicePermissions.get(packageName);
    return usbDevices != null && usbDevices.contains(device);
  }

  /** Returns true if the caller has permission to access the accessory. */
  @Implementation
  protected boolean hasPermission(UsbAccessory accessory) {
    return hasPermissionForPackage(accessory, RuntimeEnvironment.getApplication().getPackageName());
  }

  /** Returns true if the given package has permission to access the device. */
  public boolean hasPermissionForPackage(UsbAccessory accessory, String packageName) {
    List usbAccessories = grantedAccessoryPermissions.get(packageName);
    return usbAccessories != null && usbAccessories.contains(accessory);
  }

  @Implementation(minSdk = N)
  @HiddenApi
  protected void grantPermission(UsbDevice device) {
    grantPermission(device, RuntimeEnvironment.getApplication().getPackageName());
  }

  @Implementation(minSdk = N_MR1)
  @HiddenApi // SystemApi
  protected void grantPermission(UsbDevice device, String packageName) {
    List usbDevices = grantedDevicePermissions.get(packageName);
    if (usbDevices == null) {
      usbDevices = new ArrayList<>();
      grantedDevicePermissions.put(packageName, usbDevices);
    }
    usbDevices.add(device);
  }

  /** Grants permission for the accessory. */
  public void grantPermission(UsbAccessory accessory) {
    String packageName = RuntimeEnvironment.getApplication().getPackageName();
    List usbAccessories = grantedAccessoryPermissions.get(packageName);
    if (usbAccessories == null) {
      usbAccessories = new ArrayList<>();
      grantedAccessoryPermissions.put(packageName, usbAccessories);
    }
    usbAccessories.add(accessory);
  }

  /**
   * Revokes permission to a USB device granted to a package. This method does nothing if the
   * package doesn't have permission to access the device.
   */
  public void revokePermission(UsbDevice device, String packageName) {
    List usbDevices = grantedDevicePermissions.get(packageName);
    if (usbDevices != null) {
      usbDevices.remove(device);
    }
  }

  /**
   * Revokes permission to a USB accessory granted to a package. This method does nothing if the
   * package doesn't have permission to access the accessory.
   */
  public void revokePermission(UsbAccessory accessory, String packageName) {
    List usbAccessories = grantedAccessoryPermissions.get(packageName);
    if (usbAccessories != null) {
      usbAccessories.remove(accessory);
    }
  }

  /**
   * 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.
   */
  @Implementation
  protected HashMap getDeviceList() {
    return new HashMap<>(usbDevices);
  }

  @Implementation
  protected UsbAccessory[] getAccessoryList() {
    // Currently Android only supports having a single accessory attached, and if nothing
    // is attached, this method actually returns null in the real implementation.
    if (attachedUsbAccessory == null) {
      return null;
    }

    return new UsbAccessory[] {attachedUsbAccessory};
  }

  /** Sets the currently attached Usb accessory returned in #getAccessoryList. */
  public void setAttachedUsbAccessory(UsbAccessory usbAccessory) {
    this.attachedUsbAccessory = usbAccessory;
  }

  /**
   * Adds a USB device into available USB devices map with permission value. If the USB device
   * already exists, updates the USB device with new permission value.
   */
  public void addOrUpdateUsbDevice(UsbDevice usbDevice, boolean hasPermission) {
    Preconditions.checkNotNull(usbDevice);
    Preconditions.checkNotNull(usbDevice.getDeviceName());
    usbDevices.put(usbDevice.getDeviceName(), usbDevice);
    if (hasPermission) {
      grantPermission(usbDevice);
    } else {
      revokePermission(usbDevice, RuntimeEnvironment.getApplication().getPackageName());
    }
  }

  /** Removes a USB device from available USB devices map. */
  public void removeUsbDevice(UsbDevice usbDevice) {
    Preconditions.checkNotNull(usbDevice);
    usbDevices.remove(usbDevice.getDeviceName());
    revokePermission(usbDevice, RuntimeEnvironment.getApplication().getPackageName());
  }

  @Implementation(minSdk = M, maxSdk = P)
  @HiddenApi
  protected @ClassName("android.hardware.usb.UsbPort[]") Object getPorts() {
    return usbPortStatuses.keySet().toArray(new UsbPort[usbPortStatuses.size()]);
  }

  @Implementation(minSdk = Q, methodName = "getPorts")
  @HiddenApi
  protected List getPortsFromQ() {
    return new ArrayList<>(usbPortStatuses.keySet());
  }

  /** Remove all added ports from UsbManager. */
  public void clearPorts() {
    usbPorts.clear();
    usbPortStatuses.clear();
  }

  /** Adds a USB port with given ID to UsbManager. */
  public void addPort(String portId) {
    if (RuntimeEnvironment.getApiLevel() >= Q) {
      addPort(
          portId,
          UsbPortStatus.MODE_DUAL,
          UsbPortStatus.POWER_ROLE_SINK,
          UsbPortStatus.DATA_ROLE_DEVICE,
          0);
      return;
    }

    UsbPort usbPort =
        callConstructor(
            UsbPort.class,
            from(String.class, portId),
            from(int.class, getStaticField(UsbPort.class, "MODE_DUAL")));
    usbPorts.put(portId, usbPort);
    usbPortStatuses.put(
        usbPort,
        (UsbPortStatus)
            createUsbPortStatus(
                getStaticField(UsbPort.class, "MODE_DUAL"),
                getStaticField(UsbPort.class, "POWER_ROLE_SINK"),
                getStaticField(UsbPort.class, "DATA_ROLE_DEVICE"),
                0));
  }

  /** Adds a USB port with given ID and {@link UsbPortStatus} parameters to UsbManager for Q+. */
  @TargetApi(Q)
  public void addPort(
      String portId,
      int statusCurrentMode,
      int statusCurrentPowerRole,
      int statusCurrentDataRole,
      int statusSupportedRoleCombinations) {
    Preconditions.checkState(RuntimeEnvironment.getApiLevel() >= Q);
    UsbPort usbPort = (UsbPort) createUsbPort(realUsbManager, portId, statusCurrentMode);
    usbPorts.put(portId, usbPort);
    usbPortStatuses.put(
        usbPort,
        (UsbPortStatus)
            createUsbPortStatus(
                statusCurrentMode,
                statusCurrentPowerRole,
                statusCurrentDataRole,
                statusSupportedRoleCombinations));
  }

  /**
   * Returns the {@link UsbPortStatus} corresponding to the {@link UsbPort} with given {@code
   * portId} if present; otherwise returns {@code null}.
   */
  @Nullable
  public /* UsbPortStatus */ Object getPortStatus(String portId) {
    return usbPortStatuses.get(usbPorts.get(portId));
  }

  @Implementation(minSdk = M)
  @HiddenApi
  protected @ClassName("android.hardware.usb.UsbPortStatus") Object getPortStatus(
      @ClassName("android.hardware.usb.UsbPort") Object port) {
    return usbPortStatuses.get(port);
  }

  @Implementation(minSdk = M)
  @HiddenApi
  protected void setPortRoles(
      @ClassName("android.hardware.usb.UsbPort") Object port, int powerRole, int dataRole) {
    UsbPortStatus status = usbPortStatuses.get(port);
    usbPortStatuses.put(
        (UsbPort) port,
        (UsbPortStatus)
            createUsbPortStatus(
                status.getCurrentMode(),
                powerRole,
                dataRole,
                status.getSupportedRoleCombinations()));
    RuntimeEnvironment.getApplication()
        .sendBroadcast(new Intent(UsbManager.ACTION_USB_PORT_CHANGED));
  }

  /** Opens a file descriptor from a temporary file. */
  @Implementation
  protected UsbDeviceConnection openDevice(UsbDevice device) {
    return createUsbDeviceConnection(device);
  }

  /** Opens a file descriptor from a temporary file. */
  @Implementation
  protected ParcelFileDescriptor openAccessory(UsbAccessory accessory) {
    try {
      File tmpUsbDir =
          RuntimeEnvironment.getTempDirectory().createIfNotExists("usb-accessory").toFile();
      return ParcelFileDescriptor.open(
          new File(tmpUsbDir, "usb-accessory-file"), ParcelFileDescriptor.MODE_READ_WRITE);
    } catch (FileNotFoundException error) {
      throw new RuntimeException("Error shadowing openAccessory", error);
    }
  }

  /**
   * Helper method for creating a {@link UsbPortStatus}.
   *
   * 

Returns Object to avoid referencing the API M+ UsbPortStatus when running on older * platforms. */ private static Object createUsbPortStatus( int currentMode, int currentPowerRole, int currentDataRole, int supportedRoleCombinations) { if (RuntimeEnvironment.getApiLevel() >= Q) { return new UsbPortStatus( currentMode, currentPowerRole, currentDataRole, supportedRoleCombinations, 0, 0); } return callConstructor( UsbPortStatus.class, from(int.class, currentMode), from(int.class, currentPowerRole), from(int.class, currentDataRole), from(int.class, supportedRoleCombinations)); } /** * Helper method for creating a {@link UsbPort}. * *

Returns Object to avoid referencing the API M+ UsbPort when running on older platforms. */ private static Object createUsbPort(UsbManager usbManager, String id, int supportedModes) { if (RuntimeEnvironment.getApiLevel() >= Q) { return new UsbPort(usbManager, id, supportedModes, 0, false, false); } return callConstructor( UsbPort.class, from(UsbManager.class, usbManager), from(String.class, id), from(int.class, supportedModes)); } /** Helper method for creating a {@link UsbDeviceConnection}. */ private static UsbDeviceConnection createUsbDeviceConnection(UsbDevice device) { return callConstructor(UsbDeviceConnection.class, from(UsbDevice.class, device)); } /** Accessor interface for {@link UsbManager}'s internals. */ @ForType(UsbManager.class) public interface _UsbManager_ { UsbPort[] getPorts(); UsbPortStatus getPortStatus(UsbPort port); void setPortRoles(UsbPort port, int powerRole, int dataRole); } /** Accessor interface for {@link UsbManager}'s internals (Q+). */ @ForType(UsbManager.class) public interface _UsbManagerQ_ { List getPorts(); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy