Add systemApis to allow data transfer over USB-C while being docked

This change adds two systemApis to UsbPortStatus viz.,
a. getUsbDataStatus: Allows caller to infer whether USB data is enabled.
b. getPowerBrickStatus: Allows caller to infer whether a power brick
                        is connected to the USB port.

The change also adds enableUsbDataWhileDocked systemApi to UsbPort to
enable Usb data momentarily.

Bug: 211677613
Bug: 213312081
Test: Manually tested on tablet
CTS-Coverage-Bug: 215019881
Signed-off-by: Badhri Jagan Sridharan <badhri@google.com>
Change-Id: Ib7521f0f04b7f8e3e58dc253b28072d3631f21a7
This commit is contained in:
Badhri Jagan Sridharan
2021-12-27 02:50:50 -08:00
committed by Ricky Niu
parent bdbb2d15be
commit 7212c8fda4
13 changed files with 560 additions and 57 deletions

View File

@@ -4688,6 +4688,7 @@ package android.hardware.usb {
public final class UsbPort {
method @CheckResult @RequiresPermission(android.Manifest.permission.MANAGE_USB) public int enableLimitPowerTransfer(boolean);
method @CheckResult @RequiresPermission(android.Manifest.permission.MANAGE_USB) public int enableUsbData(boolean);
method @CheckResult @RequiresPermission(android.Manifest.permission.MANAGE_USB) public int enableUsbDataWhileDocked();
method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_USB) public android.hardware.usb.UsbPortStatus getStatus();
method @RequiresPermission(android.Manifest.permission.MANAGE_USB) public void setRoles(int, int);
field public static final int ENABLE_LIMIT_POWER_TRANSFER_ERROR_INTERNAL = 1; // 0x1
@@ -4700,6 +4701,12 @@ package android.hardware.usb {
field public static final int ENABLE_USB_DATA_ERROR_OTHER = 4; // 0x4
field public static final int ENABLE_USB_DATA_ERROR_PORT_MISMATCH = 3; // 0x3
field public static final int ENABLE_USB_DATA_SUCCESS = 0; // 0x0
field public static final int ENABLE_USB_DATA_WHILE_DOCKED_ERROR_DATA_ENABLED = 4; // 0x4
field public static final int ENABLE_USB_DATA_WHILE_DOCKED_ERROR_INTERNAL = 1; // 0x1
field public static final int ENABLE_USB_DATA_WHILE_DOCKED_ERROR_NOT_SUPPORTED = 2; // 0x2
field public static final int ENABLE_USB_DATA_WHILE_DOCKED_ERROR_OTHER = 5; // 0x5
field public static final int ENABLE_USB_DATA_WHILE_DOCKED_ERROR_PORT_MISMATCH = 3; // 0x3
field public static final int ENABLE_USB_DATA_WHILE_DOCKED_SUCCESS = 0; // 0x0
}
public final class UsbPortStatus implements android.os.Parcelable {
@@ -4707,7 +4714,9 @@ package android.hardware.usb {
method public int getCurrentDataRole();
method public int getCurrentMode();
method public int getCurrentPowerRole();
method public int getPowerBrickStatus();
method public int getSupportedRoleCombinations();
method @Nullable public int[] getUsbDataStatus();
method public boolean isConnected();
method public boolean isPowerTransferLimited();
method public boolean isRoleCombinationSupported(int, int);
@@ -4721,9 +4730,19 @@ package android.hardware.usb {
field public static final int MODE_DFP = 2; // 0x2
field public static final int MODE_NONE = 0; // 0x0
field public static final int MODE_UFP = 1; // 0x1
field public static final int POWER_BRICK_STATUS_CONNECTED = 1; // 0x1
field public static final int POWER_BRICK_STATUS_DISCONNECTED = 2; // 0x2
field public static final int POWER_BRICK_STATUS_UNKNOWN = 0; // 0x0
field public static final int POWER_ROLE_NONE = 0; // 0x0
field public static final int POWER_ROLE_SINK = 2; // 0x2
field public static final int POWER_ROLE_SOURCE = 1; // 0x1
field public static final int USB_DATA_STATUS_DISABLED_CONTAMINANT = 3; // 0x3
field public static final int USB_DATA_STATUS_DISABLED_DEBUG = 6; // 0x6
field public static final int USB_DATA_STATUS_DISABLED_DOCK = 4; // 0x4
field public static final int USB_DATA_STATUS_DISABLED_FORCE = 5; // 0x5
field public static final int USB_DATA_STATUS_DISABLED_OVERHEAT = 2; // 0x2
field public static final int USB_DATA_STATUS_ENABLED = 1; // 0x1
field public static final int USB_DATA_STATUS_UNKNOWN = 0; // 0x0
}
}

View File

@@ -139,6 +139,9 @@ interface IUsbManager
/* Set USB data on or off */
boolean enableUsbData(in String portId, boolean enable, int operationId, in IUsbOperationInternal callback);
/* Enable USB data when disabled due to docking event */
void enableUsbDataWhileDocked(in String portId, int operationId, in IUsbOperationInternal callback);
/* Gets the USB Hal Version. */
int getUsbHalVersion();

View File

@@ -1358,6 +1358,35 @@ public class UsbManager {
}
}
/**
* Should only be called by {@link UsbPort#enableUsbDataWhileDocked}.
* <p>
* Enables or disables USB data when disabled due to docking event.
*
* @param port USB port for which USB data needs to be enabled.
* @param operationId operationId for the request.
* @param callback callback object to be invoked when the operation is complete.
* @hide
*/
@RequiresPermission(Manifest.permission.MANAGE_USB)
void enableUsbDataWhileDocked(@NonNull UsbPort port, int operationId,
IUsbOperationInternal callback) {
Objects.requireNonNull(port, "enableUsbDataWhileDocked: port must not be null. opId:"
+ operationId);
try {
mService.enableUsbDataWhileDocked(port.getId(), operationId, callback);
} catch (RemoteException e) {
Log.e(TAG, "enableUsbDataWhileDocked: failed. opId:" + operationId, e);
try {
callback.onOperationComplete(UsbOperationInternal.USB_OPERATION_ERROR_INTERNAL);
} catch (RemoteException r) {
Log.e(TAG, "enableUsbDataWhileDocked: failed to call onOperationComplete. opId:"
+ operationId, r);
}
throw e.rethrowFromSystemServer();
}
}
/**
* Sets the component that will handle USB device connection.
* <p>

View File

@@ -33,9 +33,19 @@ import static android.hardware.usb.UsbPortStatus.MODE_DFP;
import static android.hardware.usb.UsbPortStatus.MODE_DUAL;
import static android.hardware.usb.UsbPortStatus.MODE_NONE;
import static android.hardware.usb.UsbPortStatus.MODE_UFP;
import static android.hardware.usb.UsbPortStatus.POWER_BRICK_STATUS_DISCONNECTED;
import static android.hardware.usb.UsbPortStatus.POWER_BRICK_STATUS_UNKNOWN;
import static android.hardware.usb.UsbPortStatus.POWER_BRICK_STATUS_CONNECTED;
import static android.hardware.usb.UsbPortStatus.POWER_ROLE_NONE;
import static android.hardware.usb.UsbPortStatus.POWER_ROLE_SINK;
import static android.hardware.usb.UsbPortStatus.POWER_ROLE_SOURCE;
import static android.hardware.usb.UsbPortStatus.USB_DATA_STATUS_UNKNOWN;
import static android.hardware.usb.UsbPortStatus.USB_DATA_STATUS_ENABLED;
import static android.hardware.usb.UsbPortStatus.USB_DATA_STATUS_DISABLED_OVERHEAT;
import static android.hardware.usb.UsbPortStatus.USB_DATA_STATUS_DISABLED_CONTAMINANT;
import static android.hardware.usb.UsbPortStatus.USB_DATA_STATUS_DISABLED_DOCK;
import static android.hardware.usb.UsbPortStatus.USB_DATA_STATUS_DISABLED_FORCE;
import static android.hardware.usb.UsbPortStatus.USB_DATA_STATUS_DISABLED_DEBUG;
import android.Manifest;
import android.annotation.CheckResult;
@@ -154,6 +164,48 @@ public final class UsbPort {
@Retention(RetentionPolicy.SOURCE)
@interface EnableLimitPowerTransferStatus{}
/**
* The {@link #enableUsbDataWhileDocked} request was successfully completed.
*/
public static final int ENABLE_USB_DATA_WHILE_DOCKED_SUCCESS = 0;
/**
* The {@link #enableUsbDataWhileDocked} request failed due to internal error.
*/
public static final int ENABLE_USB_DATA_WHILE_DOCKED_ERROR_INTERNAL = 1;
/**
* The {@link #enableUsbDataWhileDocked} request failed as it's not supported.
*/
public static final int ENABLE_USB_DATA_WHILE_DOCKED_ERROR_NOT_SUPPORTED = 2;
/**
* The {@link #enableUsbDataWhileDocked} request failed as port id mismatched.
*/
public static final int ENABLE_USB_DATA_WHILE_DOCKED_ERROR_PORT_MISMATCH = 3;
/**
* The {@link #enableUsbDataWhileDocked} request failed as data is still enabled.
*/
public static final int ENABLE_USB_DATA_WHILE_DOCKED_ERROR_DATA_ENABLED = 4;
/**
* The {@link #enableUsbDataWhileDocked} request failed due to other reasons.
*/
public static final int ENABLE_USB_DATA_WHILE_DOCKED_ERROR_OTHER = 5;
/** @hide */
@IntDef(prefix = { "ENABLE_USB_DATA_WHILE_DOCKED_" }, value = {
ENABLE_USB_DATA_WHILE_DOCKED_SUCCESS,
ENABLE_USB_DATA_WHILE_DOCKED_ERROR_INTERNAL,
ENABLE_USB_DATA_WHILE_DOCKED_ERROR_NOT_SUPPORTED,
ENABLE_USB_DATA_WHILE_DOCKED_ERROR_PORT_MISMATCH,
ENABLE_USB_DATA_WHILE_DOCKED_ERROR_DATA_ENABLED,
ENABLE_USB_DATA_WHILE_DOCKED_ERROR_OTHER
})
@Retention(RetentionPolicy.SOURCE)
@interface EnableUsbDataWhileDockedStatus{}
/** @hide */
public UsbPort(@NonNull UsbManager usbManager, @NonNull String id, int supportedModes,
int supportedContaminantProtectionModes,
@@ -307,6 +359,51 @@ public final class UsbPort {
}
}
/**
* Enables Usb data when disabled due to {@link UsbPort#USB_DATA_STATUS_DISABLED_DOCK}
*
* @return {@link #ENABLE_USB_DATA_WHILE_DOCKED_SUCCESS} when request completes successfully or
* {@link #ENABLE_USB_DATA_WHILE_DOCKED_ERROR_INTERNAL} when request fails due to
* internal error or
* {@link ENABLE_USB_DATA_WHILE_DOCKED_ERROR_NOT_SUPPORTED} when not supported or
* {@link ENABLE_USB_DATA_WHILE_DOCKED_ERROR_PORT_MISMATCH} when request fails due to
* port id mismatch or
* {@link ENABLE_USB_DATA_WHILE_DOCKED_ERROR_DATA_ENABLED} when request fails as data
* is still enabled or
* {@link ENABLE_USB_DATA_WHILE_DOCKED_ERROR_OTHER} when fails due to other reasons.
*/
@CheckResult
@RequiresPermission(Manifest.permission.MANAGE_USB)
public @EnableUsbDataWhileDockedStatus int enableUsbDataWhileDocked() {
// UID is added To minimize operationID overlap between two different packages.
int operationId = sUsbOperationCount.incrementAndGet() + Binder.getCallingUid();
Log.i(TAG, "enableUsbData opId:" + operationId
+ " callingUid:" + Binder.getCallingUid());
UsbPortStatus portStatus = getStatus();
if (portStatus != null &&
!usbDataStatusToString(portStatus.getUsbDataStatus()).contains("disabled-dock")) {
return ENABLE_USB_DATA_WHILE_DOCKED_ERROR_DATA_ENABLED;
}
UsbOperationInternal opCallback =
new UsbOperationInternal(operationId, mId);
mUsbManager.enableUsbDataWhileDocked(this, operationId, opCallback);
opCallback.waitForOperationComplete();
int result = opCallback.getStatus();
switch (result) {
case USB_OPERATION_SUCCESS:
return ENABLE_USB_DATA_WHILE_DOCKED_SUCCESS;
case USB_OPERATION_ERROR_INTERNAL:
return ENABLE_USB_DATA_WHILE_DOCKED_ERROR_INTERNAL;
case USB_OPERATION_ERROR_NOT_SUPPORTED:
return ENABLE_USB_DATA_WHILE_DOCKED_ERROR_NOT_SUPPORTED;
case USB_OPERATION_ERROR_PORT_MISMATCH:
return ENABLE_USB_DATA_WHILE_DOCKED_ERROR_PORT_MISMATCH;
default:
return ENABLE_USB_DATA_WHILE_DOCKED_ERROR_OTHER;
}
}
/**
* Limits power transfer In and out of the port.
* <p>
@@ -445,6 +542,57 @@ public final class UsbPort {
}
}
/** @hide */
public static String usbDataStatusToString(int usbDataStatus) {
switch (usbDataStatus) {
case USB_DATA_STATUS_UNKNOWN:
return "unknown";
case USB_DATA_STATUS_ENABLED:
return "enabled";
case USB_DATA_STATUS_DISABLED_OVERHEAT:
return "disabled-overheat";
case USB_DATA_STATUS_DISABLED_CONTAMINANT:
return "disabled-contaminant";
case USB_DATA_STATUS_DISABLED_DOCK:
return "disabled-dock";
case USB_DATA_STATUS_DISABLED_FORCE:
return "disabled-force";
case USB_DATA_STATUS_DISABLED_DEBUG:
return "disabled-debug";
default:
return Integer.toString(usbDataStatus);
}
}
/** @hide */
public static String usbDataStatusToString(int[] usbDataStatus) {
StringBuilder modeString = new StringBuilder();
if (usbDataStatus == null) {
return "unknown";
}
for (int i = 0; i < usbDataStatus.length; i++) {
modeString.append(usbDataStatusToString(usbDataStatus[i]));
if (i < usbDataStatus.length - 1) {
modeString.append(", ");
}
}
return modeString.toString();
}
/** @hide */
public static String powerBrickStatusToString(int powerBrickStatus) {
switch (powerBrickStatus) {
case POWER_BRICK_STATUS_UNKNOWN:
return "unknown";
case POWER_BRICK_STATUS_CONNECTED:
return "connected";
case POWER_BRICK_STATUS_DISCONNECTED:
return "disconnected";
default:
return Integer.toString(powerBrickStatus);
}
}
/** @hide */
public static String roleCombinationsToString(int combo) {
StringBuilder result = new StringBuilder();

View File

@@ -18,6 +18,7 @@ package android.hardware.usb;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -42,8 +43,9 @@ public final class UsbPortStatus implements Parcelable {
private final int mSupportedRoleCombinations;
private final @ContaminantProtectionStatus int mContaminantProtectionStatus;
private final @ContaminantDetectionStatus int mContaminantDetectionStatus;
private final boolean mUsbDataEnabled;
private final boolean mPowerTransferLimited;
private final @UsbDataStatus int[] mUsbDataStatus;
private final @PowerBrickStatus int mPowerBrickStatus;
/**
* Power role: This USB port does not have a power role.
@@ -193,6 +195,57 @@ public final class UsbPortStatus implements Parcelable {
*/
public static final int CONTAMINANT_PROTECTION_DISABLED = 1 << 3;
/**
* USB data status is not known.
*/
public static final int USB_DATA_STATUS_UNKNOWN = 0;
/**
* USB data is enabled.
*/
public static final int USB_DATA_STATUS_ENABLED = 1;
/**
* USB data is disabled as the port is too hot.
*/
public static final int USB_DATA_STATUS_DISABLED_OVERHEAT = 2;
/**
* USB data is disabled due to contaminated port.
*/
public static final int USB_DATA_STATUS_DISABLED_CONTAMINANT = 3;
/**
* USB data is disabled due to docking event.
*/
public static final int USB_DATA_STATUS_DISABLED_DOCK = 4;
/**
* USB data is disabled by
* {@link UsbPort#enableUsbData UsbPort.enableUsbData}.
*/
public static final int USB_DATA_STATUS_DISABLED_FORCE = 5;
/**
* USB data is disabled for debug.
*/
public static final int USB_DATA_STATUS_DISABLED_DEBUG = 6;
/**
* Unknown whether a power brick is connected.
*/
public static final int POWER_BRICK_STATUS_UNKNOWN = 0;
/**
* The connected device is a power brick.
*/
public static final int POWER_BRICK_STATUS_CONNECTED = 1;
/**
* The connected device is not power brick.
*/
public static final int POWER_BRICK_STATUS_DISCONNECTED = 2;
@IntDef(prefix = { "CONTAMINANT_DETECTION_" }, value = {
CONTAMINANT_DETECTION_NOT_SUPPORTED,
CONTAMINANT_DETECTION_DISABLED,
@@ -222,19 +275,42 @@ public final class UsbPortStatus implements Parcelable {
@Retention(RetentionPolicy.SOURCE)
@interface UsbPortMode{}
/** @hide */
@IntDef(prefix = { "USB_DATA_STATUS_" }, value = {
USB_DATA_STATUS_UNKNOWN,
USB_DATA_STATUS_ENABLED,
USB_DATA_STATUS_DISABLED_OVERHEAT,
USB_DATA_STATUS_DISABLED_CONTAMINANT,
USB_DATA_STATUS_DISABLED_DOCK,
USB_DATA_STATUS_DISABLED_FORCE,
USB_DATA_STATUS_DISABLED_DEBUG
})
@Retention(RetentionPolicy.SOURCE)
@interface UsbDataStatus{}
/** @hide */
@IntDef(prefix = { "POWER_BRICK_STATUS_" }, value = {
POWER_BRICK_STATUS_UNKNOWN,
POWER_BRICK_STATUS_DISCONNECTED,
POWER_BRICK_STATUS_CONNECTED,
})
@Retention(RetentionPolicy.SOURCE)
@interface PowerBrickStatus{}
/** @hide */
public UsbPortStatus(int currentMode, int currentPowerRole, int currentDataRole,
int supportedRoleCombinations, int contaminantProtectionStatus,
int contaminantDetectionStatus, boolean usbDataEnabled,
boolean powerTransferLimited) {
int contaminantDetectionStatus, @UsbDataStatus int[] usbDataStatus,
boolean powerTransferLimited, @PowerBrickStatus int powerBrickStatus) {
mCurrentMode = currentMode;
mCurrentPowerRole = currentPowerRole;
mCurrentDataRole = currentDataRole;
mSupportedRoleCombinations = supportedRoleCombinations;
mContaminantProtectionStatus = contaminantProtectionStatus;
mContaminantDetectionStatus = contaminantDetectionStatus;
mUsbDataEnabled = usbDataEnabled;
mUsbDataStatus = usbDataStatus;
mPowerTransferLimited = powerTransferLimited;
mPowerBrickStatus = powerBrickStatus;
}
/** @hide */
@@ -247,7 +323,8 @@ public final class UsbPortStatus implements Parcelable {
mSupportedRoleCombinations = supportedRoleCombinations;
mContaminantProtectionStatus = contaminantProtectionStatus;
mContaminantDetectionStatus = contaminantDetectionStatus;
mUsbDataEnabled = true;
mUsbDataStatus = new int[]{USB_DATA_STATUS_UNKNOWN};
mPowerBrickStatus = POWER_BRICK_STATUS_UNKNOWN;
mPowerTransferLimited = false;
}
@@ -334,10 +411,14 @@ public final class UsbPortStatus implements Parcelable {
/**
* Returns UsbData status.
*
* @hide
* @return Current USB data status of the port: {@link #USB_DATA_STATUS_UNKNOWN}
* or {@link #USB_DATA_STATUS_ENABLED} or {@link #USB_DATA_STATUS_DIASBLED_OVERHEAT}
* or {@link #USB_DATA_STATUS_DISABLED_CONTAMINANT}
* or {@link #USB_DATA_STATUS_DISABLED_DOCK} or {@link #USB_DATA_STATUS_DISABLED_FORCE}
* or {@link #USB_DATA_STATUS_DISABLED_DEBUG}
*/
public boolean getUsbDataStatus() {
return mUsbDataEnabled;
public @UsbDataStatus @Nullable int[] getUsbDataStatus() {
return mUsbDataStatus;
}
/**
@@ -350,6 +431,17 @@ public final class UsbPortStatus implements Parcelable {
return mPowerTransferLimited;
}
/**
* Let's the caller know if a power brick is connected to the USB port.
*
* @return {@link #POWER_BRICK_STATUS_UNKNOWN}
* or {@link #POWER_BRICK_STATUS_CONNECTED}
* or {@link #POWER_BRICK_STATUS_DISCONNECTED}
*/
public @PowerBrickStatus int getPowerBrickStatus() {
return mPowerBrickStatus;
}
@NonNull
@Override
public String toString() {
@@ -363,10 +455,12 @@ public final class UsbPortStatus implements Parcelable {
+ getContaminantDetectionStatus()
+ ", contaminantProtectionStatus="
+ getContaminantProtectionStatus()
+ ", usbDataEnabled="
+ getUsbDataStatus()
+ ", usbDataStatus="
+ UsbPort.usbDataStatusToString(getUsbDataStatus())
+ ", isPowerTransferLimited="
+ isPowerTransferLimited()
+", powerBrickStatus="
+ UsbPort.powerBrickStatusToString(getPowerBrickStatus())
+ "}";
}
@@ -383,8 +477,10 @@ public final class UsbPortStatus implements Parcelable {
dest.writeInt(mSupportedRoleCombinations);
dest.writeInt(mContaminantProtectionStatus);
dest.writeInt(mContaminantDetectionStatus);
dest.writeBoolean(mUsbDataEnabled);
dest.writeInt(mUsbDataStatus.length);
dest.writeIntArray(mUsbDataStatus);
dest.writeBoolean(mPowerTransferLimited);
dest.writeInt(mPowerBrickStatus);
}
public static final @NonNull Parcelable.Creator<UsbPortStatus> CREATOR =
@@ -397,11 +493,14 @@ public final class UsbPortStatus implements Parcelable {
int supportedRoleCombinations = in.readInt();
int contaminantProtectionStatus = in.readInt();
int contaminantDetectionStatus = in.readInt();
boolean usbDataEnabled = in.readBoolean();
int[] usbDataStatus = new int[in.readInt()];
in.readIntArray(usbDataStatus);
boolean powerTransferLimited = in.readBoolean();
int powerBrickStatus = in.readInt();
return new UsbPortStatus(currentMode, currentPowerRole, currentDataRole,
supportedRoleCombinations, contaminantProtectionStatus,
contaminantDetectionStatus, usbDataEnabled, powerTransferLimited);
contaminantDetectionStatus, usbDataStatus, powerTransferLimited,
powerBrickStatus);
}
@Override

View File

@@ -244,10 +244,12 @@ public class DumpUtils {
writeContaminantPresenceStatus(dump, "contaminant_presence_status",
UsbPortStatusProto.CONTAMINANT_PRESENCE_STATUS,
status.getContaminantDetectionStatus());
dump.write("usb_data_enabled", UsbPortStatusProto.USB_DATA_ENABLED,
status.getUsbDataStatus());
dump.write("usb_data_status", UsbPortStatusProto.USB_DATA_STATUS,
UsbPort.usbDataStatusToString(status.getUsbDataStatus()));
dump.write("is_power_transfer_limited", UsbPortStatusProto.IS_POWER_TRANSFER_LIMITED,
status.isPowerTransferLimited());
dump.write("usb_power_brick_status", UsbPortStatusProto.USB_POWER_BRICK_STATUS,
UsbPort.powerBrickStatusToString(status.getPowerBrickStatus()));
dump.end(token);
}
}

View File

@@ -264,8 +264,9 @@ message UsbPortStatusProto {
optional DataRole data_role = 4;
repeated UsbPortStatusRoleCombinationProto role_combinations = 5;
optional android.service.ContaminantPresenceStatus contaminant_presence_status = 6;
optional bool usb_data_enabled = 7;
optional string usb_data_status = 7;
optional bool is_power_transfer_limited = 8;
optional string usb_power_brick_status = 9;
}
message UsbPortStatusRoleCombinationProto {

View File

@@ -383,6 +383,46 @@ public class UsbPortManager {
}
}
/**
* Enables USB data when disabled due to {@link UsbPortStatus#USB_DATA_STATUS_DISABLED_DOCK}
*/
public void enableUsbDataWhileDocked(@NonNull String portId, long transactionId,
IUsbOperationInternal callback, IndentingPrintWriter pw) {
Objects.requireNonNull(portId);
final PortInfo portInfo = mPorts.get(portId);
if (portInfo == null) {
logAndPrint(Log.ERROR, pw, "enableUsbDataWhileDocked: No such port: " + portId
+ " opId:" + transactionId);
try {
if (callback != null) {
callback.onOperationComplete(USB_OPERATION_ERROR_PORT_MISMATCH);
}
} catch (RemoteException e) {
logAndPrintException(pw,
"enableUsbDataWhileDocked: Failed to call OperationComplete. opId:"
+ transactionId, e);
}
return;
}
try {
try {
mUsbPortHal.enableUsbDataWhileDocked(portId, transactionId, callback);
} catch (Exception e) {
logAndPrintException(pw,
"enableUsbDataWhileDocked: Failed to limit power transfer. opId:"
+ transactionId , e);
if (callback != null) {
callback.onOperationComplete(USB_OPERATION_ERROR_INTERNAL);
}
}
} catch (RemoteException e) {
logAndPrintException(pw,
"enableUsbDataWhileDocked:Failed to call onOperationComplete. opId:"
+ transactionId, e);
}
}
/**
* Enable/disable the USB data signaling
*
@@ -759,8 +799,9 @@ public class UsbPortManager {
portInfo.contaminantProtectionStatus,
portInfo.supportsEnableContaminantPresenceDetection,
portInfo.contaminantDetectionStatus,
portInfo.usbDataEnabled,
portInfo.powerTransferLimited, pw);
portInfo.usbDataStatus,
portInfo.powerTransferLimited,
portInfo.powerBrickStatus, pw);
}
} else {
for (RawPortInfo currentPortInfo : newPortInfo) {
@@ -773,8 +814,9 @@ public class UsbPortManager {
currentPortInfo.contaminantProtectionStatus,
currentPortInfo.supportsEnableContaminantPresenceDetection,
currentPortInfo.contaminantDetectionStatus,
currentPortInfo.usbDataEnabled,
currentPortInfo.powerTransferLimited, pw);
currentPortInfo.usbDataStatus,
currentPortInfo.powerTransferLimited,
currentPortInfo.powerBrickStatus, pw);
}
}
@@ -810,8 +852,9 @@ public class UsbPortManager {
int contaminantProtectionStatus,
boolean supportsEnableContaminantPresenceDetection,
int contaminantDetectionStatus,
boolean usbDataEnabled,
int[] usbDataStatus,
boolean powerTransferLimited,
int powerBrickStatus,
IndentingPrintWriter pw) {
// Only allow mode switch capability for dual role ports.
// Validate that the current mode matches the supported modes we expect.
@@ -870,8 +913,8 @@ public class UsbPortManager {
currentPowerRole, canChangePowerRole,
currentDataRole, canChangeDataRole,
supportedRoleCombinations, contaminantProtectionStatus,
contaminantDetectionStatus, usbDataEnabled,
powerTransferLimited);
contaminantDetectionStatus, usbDataStatus,
powerTransferLimited, powerBrickStatus);
mPorts.put(portId, portInfo);
} else {
// Validate that ports aren't changing definition out from under us.
@@ -908,8 +951,8 @@ public class UsbPortManager {
currentPowerRole, canChangePowerRole,
currentDataRole, canChangeDataRole,
supportedRoleCombinations, contaminantProtectionStatus,
contaminantDetectionStatus, usbDataEnabled,
powerTransferLimited)) {
contaminantDetectionStatus, usbDataStatus,
powerTransferLimited, powerBrickStatus)) {
portInfo.mDisposition = PortInfo.DISPOSITION_CHANGED;
} else {
portInfo.mDisposition = PortInfo.DISPOSITION_READY;
@@ -1135,7 +1178,9 @@ public class UsbPortManager {
!= supportedRoleCombinations) {
mUsbPortStatus = new UsbPortStatus(currentMode, currentPowerRole, currentDataRole,
supportedRoleCombinations, UsbPortStatus.CONTAMINANT_PROTECTION_NONE,
UsbPortStatus.CONTAMINANT_DETECTION_NOT_SUPPORTED, true, false);
UsbPortStatus.CONTAMINANT_DETECTION_NOT_SUPPORTED,
new int[]{UsbPortStatus.USB_DATA_STATUS_UNKNOWN}, false,
UsbPortStatus.POWER_BRICK_STATUS_UNKNOWN);
dispositionChanged = true;
}
@@ -1150,12 +1195,31 @@ public class UsbPortManager {
return dispositionChanged;
}
private boolean dataStatusEquals(int[] dataStatusL, int[] dataStatusR) {
if (dataStatusL == null && dataStatusR == null) {
return true;
}
if ((dataStatusL == null && dataStatusR != null)
|| (dataStatusL != null && dataStatusR == null)) {
return false;
}
if (dataStatusL.length != dataStatusR.length) {
return false;
}
for (int i = 0; i < dataStatusL.length; i++) {
if (dataStatusL[i] != dataStatusR[i]) {
return false;
}
}
return true;
}
public boolean setStatus(int currentMode, boolean canChangeMode,
int currentPowerRole, boolean canChangePowerRole,
int currentDataRole, boolean canChangeDataRole,
int supportedRoleCombinations, int contaminantProtectionStatus,
int contaminantDetectionStatus, boolean usbDataEnabled,
boolean powerTransferLimited) {
int contaminantDetectionStatus, int[] usbDataStatus,
boolean powerTransferLimited, int powerBrickStatus) {
boolean dispositionChanged = false;
mCanChangeMode = canChangeMode;
@@ -1171,14 +1235,15 @@ public class UsbPortManager {
!= contaminantProtectionStatus
|| mUsbPortStatus.getContaminantDetectionStatus()
!= contaminantDetectionStatus
|| mUsbPortStatus.getUsbDataStatus()
!= usbDataEnabled
|| !dataStatusEquals(mUsbPortStatus.getUsbDataStatus(), usbDataStatus)
|| mUsbPortStatus.isPowerTransferLimited()
!= powerTransferLimited) {
!= powerTransferLimited
|| mUsbPortStatus.getPowerBrickStatus()
!= powerBrickStatus) {
mUsbPortStatus = new UsbPortStatus(currentMode, currentPowerRole, currentDataRole,
supportedRoleCombinations, contaminantProtectionStatus,
contaminantDetectionStatus, usbDataEnabled,
powerTransferLimited);
contaminantDetectionStatus, usbDataStatus,
powerTransferLimited, powerBrickStatus);
dispositionChanged = true;
}

View File

@@ -813,6 +813,32 @@ public class UsbService extends IUsbManager.Stub {
return wait;
}
@Override
public void enableUsbDataWhileDocked(String portId, int operationId,
IUsbOperationInternal callback) {
Objects.requireNonNull(portId, "enableUsbDataWhileDocked: portId must not be null. opId:"
+ operationId);
Objects.requireNonNull(callback,
"enableUsbDataWhileDocked: callback must not be null. opId:"
+ operationId);
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
final long ident = Binder.clearCallingIdentity();
boolean wait;
try {
if (mPortManager != null) {
mPortManager.enableUsbDataWhileDocked(portId, operationId, callback, null);
} else {
try {
callback.onOperationComplete(USB_OPERATION_ERROR_INTERNAL);
} catch (RemoteException e) {
Slog.e(TAG, "enableUsbData: Failed to call onOperationComplete", e);
}
}
} finally {
Binder.restoreCallingIdentity(ident);
}
}
@Override
public void setUsbDeviceConnectionHandler(ComponentName usbDeviceConnectionHandler) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);

View File

@@ -37,8 +37,9 @@ public final class RawPortInfo implements Parcelable {
public int contaminantProtectionStatus;
public boolean supportsEnableContaminantPresenceDetection;
public int contaminantDetectionStatus;
public boolean usbDataEnabled;
public int[] usbDataStatus;
public boolean powerTransferLimited;
public int powerBrickStatus;
public RawPortInfo(String portId, int supportedModes) {
this.portId = portId;
@@ -48,8 +49,9 @@ public final class RawPortInfo implements Parcelable {
this.contaminantProtectionStatus = UsbPortStatus.CONTAMINANT_PROTECTION_NONE;
this.supportsEnableContaminantPresenceDetection = false;
this.contaminantDetectionStatus = UsbPortStatus.CONTAMINANT_DETECTION_NOT_SUPPORTED;
this.usbDataEnabled = true;
this.usbDataStatus[0] = UsbPortStatus.USB_DATA_STATUS_UNKNOWN;
this.powerTransferLimited = false;
this.powerBrickStatus = UsbPortStatus.POWER_BRICK_STATUS_UNKNOWN;
}
public RawPortInfo(String portId, int supportedModes, int supportedContaminantProtectionModes,
@@ -60,8 +62,9 @@ public final class RawPortInfo implements Parcelable {
int contaminantProtectionStatus,
boolean supportsEnableContaminantPresenceDetection,
int contaminantDetectionStatus,
boolean usbDataEnabled,
boolean powerTransferLimited) {
int[] usbDataStatus,
boolean powerTransferLimited,
int powerBrickStatus) {
this.portId = portId;
this.supportedModes = supportedModes;
this.supportedContaminantProtectionModes = supportedContaminantProtectionModes;
@@ -77,8 +80,9 @@ public final class RawPortInfo implements Parcelable {
this.supportsEnableContaminantPresenceDetection =
supportsEnableContaminantPresenceDetection;
this.contaminantDetectionStatus = contaminantDetectionStatus;
this.usbDataEnabled = usbDataEnabled;
this.usbDataStatus = usbDataStatus;
this.powerTransferLimited = powerTransferLimited;
this.powerBrickStatus = powerBrickStatus;
}
@Override
@@ -101,8 +105,10 @@ public final class RawPortInfo implements Parcelable {
dest.writeInt(contaminantProtectionStatus);
dest.writeBoolean(supportsEnableContaminantPresenceDetection);
dest.writeInt(contaminantDetectionStatus);
dest.writeBoolean(usbDataEnabled);
dest.writeInt(usbDataStatus.length);
dest.writeIntArray(usbDataStatus);
dest.writeBoolean(powerTransferLimited);
dest.writeInt(powerBrickStatus);
}
public static final Parcelable.Creator<RawPortInfo> CREATOR =
@@ -122,8 +128,10 @@ public final class RawPortInfo implements Parcelable {
int contaminantProtectionStatus = in.readInt();
boolean supportsEnableContaminantPresenceDetection = in.readBoolean();
int contaminantDetectionStatus = in.readInt();
boolean usbDataEnabled = in.readBoolean();
int[] usbDataStatus = new int[in.readInt()];
in.readIntArray(usbDataStatus);
boolean powerTransferLimited = in.readBoolean();
int powerBrickStatus = in.readInt();
return new RawPortInfo(id, supportedModes,
supportedContaminantProtectionModes, currentMode, canChangeMode,
currentPowerRole, canChangePowerRole,
@@ -131,8 +139,8 @@ public final class RawPortInfo implements Parcelable {
supportsEnableContaminantPresenceProtection,
contaminantProtectionStatus,
supportsEnableContaminantPresenceDetection,
contaminantDetectionStatus, usbDataEnabled,
powerTransferLimited);
contaminantDetectionStatus, usbDataStatus,
powerTransferLimited, powerBrickStatus);
}
@Override

View File

@@ -322,6 +322,48 @@ public final class UsbPortAidl implements UsbPortHal {
}
}
@Override
public void enableUsbDataWhileDocked(String portName, long operationID,
IUsbOperationInternal callback) {
Objects.requireNonNull(portName);
long key = operationID;
synchronized (mLock) {
try {
if (mProxy == null) {
logAndPrint(Log.ERROR, mPw,
"enableUsbDataWhileDocked: Proxy is null. Retry !opID:"
+ operationID);
callback.onOperationComplete(USB_OPERATION_ERROR_INTERNAL);
return;
}
while (sCallbacks.get(key) != null) {
key = ThreadLocalRandom.current().nextInt();
}
if (key != operationID) {
logAndPrint(Log.INFO, mPw,
"enableUsbDataWhileDocked: operationID exists ! opID:"
+ operationID + " key:" + key);
}
try {
sCallbacks.put(key, callback);
mProxy.enableUsbDataWhileDocked(portName, key);
} catch (RemoteException e) {
logAndPrintException(mPw,
"enableUsbDataWhileDocked: error while invoking hal"
+ "portID=" + portName + " opID:" + operationID, e);
if (callback != null) {
callback.onOperationComplete(USB_OPERATION_ERROR_INTERNAL);
}
sCallbacks.remove(key);
}
} catch (RemoteException e) {
logAndPrintException(mPw,
"enableUsbDataWhileDocked: Failed to call onOperationComplete portID="
+ portName + " opID:" + operationID, e);
}
}
}
private static class HALCallback extends IUsbCallback.Stub {
public IndentingPrintWriter mPw;
public UsbPortManager mPortManager;
@@ -403,6 +445,14 @@ public final class UsbPortAidl implements UsbPortHal {
return supportedContaminantProtectionModes;
}
private int[] toIntArray(byte[] input) {
int[] output = new int[input.length];
for (int i = 0; i < input.length; i++) {
output[i] = input[i];
}
return output;
}
@Override
public void notifyPortStatusChange(
android.hardware.usb.PortStatus[] currentPortStatus, int retval) {
@@ -434,8 +484,9 @@ public final class UsbPortAidl implements UsbPortHal {
toContaminantProtectionStatus(current.contaminantProtectionStatus),
current.supportsEnableContaminantPresenceDetection,
current.contaminantDetectionStatus,
current.usbDataEnabled,
current.powerTransferLimited);
toIntArray(current.usbDataStatus),
current.powerTransferLimited,
current.powerBrickStatus);
newPortInfo.add(temp);
UsbPortManager.logAndPrint(Log.INFO, mPw, "ClientCallback AIDL V1: "
+ current.portName);
@@ -529,5 +580,30 @@ public final class UsbPortAidl implements UsbPortHal {
e);
}
}
@Override
public void notifyEnableUsbDataWhileDockedStatus(String portName, int retval,
long operationID) {
if (retval == Status.SUCCESS) {
UsbPortManager.logAndPrint(Log.INFO, mPw, portName + ": opID:"
+ operationID + " successful");
} else {
UsbPortManager.logAndPrint(Log.ERROR, mPw, portName
+ "notifyEnableUsbDataWhileDockedStatus: opID:"
+ operationID + " failed. err:" + retval);
}
try {
IUsbOperationInternal callback = sCallbacks.get(operationID);
if (callback != null) {
sCallbacks.get(operationID).onOperationComplete(retval == Status.SUCCESS
? USB_OPERATION_SUCCESS
: USB_OPERATION_ERROR_INTERNAL);
}
} catch (RemoteException e) {
logAndPrintException(mPw,
"notifyEnableUsbDataWhileDockedStatus: Failed to call onOperationComplete",
e);
}
}
}
}

View File

@@ -158,15 +158,27 @@ public interface UsbPortHal {
* completion.
* @param callback callback object to be invoked when the operation is complete.
* @return True when the operation is asynchronous. The caller of
* {@link UsbOperationCallbackInternal} must therefore call
* {@link UsbOperationCallbackInternal#waitForOperationComplete} for processing
* {@link UsbOperationInternal} must therefore call
* {@link UsbOperationInternal#waitForOperationComplete} for processing
* the result.
* False when the operation is synchronous. Caller can proceed reading the result
* through {@link UsbOperationCallbackInternal#getStatus}
* through {@link UsbOperationInternal#getStatus}
*/
public boolean enableUsbData(String portName, boolean enable, long transactionId,
IUsbOperationInternal callback);
/**
* Invoked to enable UsbData when disabled due to docking event.
*
* @param portName Port Identifier.
* @param transactionId Used for tracking the current request and is passed down to the HAL
* implementation as needed.
* @param callback callback object to be invoked to invoke the status of the operation upon
* completion.
*/
public void enableUsbDataWhileDocked(String portName, long transactionId,
IUsbOperationInternal callback);
/**
* Invoked to enableLimitPowerTransfer on the specified port.
*

View File

@@ -30,8 +30,12 @@ import static android.hardware.usb.UsbPortStatus.DATA_ROLE_HOST;
import static android.hardware.usb.UsbPortStatus.MODE_DFP;
import static android.hardware.usb.UsbPortStatus.MODE_DUAL;
import static android.hardware.usb.UsbPortStatus.MODE_UFP;
import static android.hardware.usb.UsbPortStatus.POWER_BRICK_STATUS_UNKNOWN;
import static android.hardware.usb.UsbPortStatus.POWER_ROLE_SINK;
import static android.hardware.usb.UsbPortStatus.POWER_ROLE_SOURCE;
import static android.hardware.usb.UsbPortStatus.USB_DATA_STATUS_DISABLED_FORCE;
import static android.hardware.usb.UsbPortStatus.USB_DATA_STATUS_UNKNOWN;
import static com.android.server.usb.UsbPortManager.logAndPrint;
import static com.android.server.usb.UsbPortManager.logAndPrintException;
@@ -40,6 +44,7 @@ import android.annotation.Nullable;
import android.hardware.usb.IUsbOperationInternal;
import android.hardware.usb.UsbManager.UsbHalVersion;
import android.hardware.usb.UsbPort;
import android.hardware.usb.UsbPortStatus;
import android.hardware.usb.V1_0.IUsb;
import android.hardware.usb.V1_0.PortRoleType;
import android.hardware.usb.V1_0.Status;
@@ -80,7 +85,7 @@ public final class UsbPortHidl implements UsbPortHal {
private HALCallback mHALCallback;
private boolean mSystemReady;
// Workaround since HIDL HAL versions report UsbDataEnabled status in UsbPortStatus;
private static boolean sUsbDataEnabled = true;
private static int sUsbDataStatus = USB_DATA_STATUS_UNKNOWN;
public @UsbHalVersion int getUsbHalVersion() throws RemoteException {
int version;
@@ -281,6 +286,17 @@ public final class UsbPortHidl implements UsbPortHal {
}
}
@Override
public void enableUsbDataWhileDocked(String portName, long transactionId,
IUsbOperationInternal callback) {
/* Not supported in HIDL hals*/
try {
callback.onOperationComplete(USB_OPERATION_ERROR_NOT_SUPPORTED);
} catch (RemoteException e) {
logAndPrintException(mPw, "Failed to call onOperationComplete", e);
}
}
@Override
public void switchDataRole(String portId, @HalUsbDataRole int newDataRole, long transactionId) {
synchronized (mLock) {
@@ -332,7 +348,7 @@ public final class UsbPortHidl implements UsbPortHal {
android.hardware.usb.V1_3.IUsb proxy
= android.hardware.usb.V1_3.IUsb.castFrom(mProxy);
success = proxy.enableUsbDataSignal(enable);
} catch (RemoteException e) {
} catch (RemoteException e) {
logAndPrintException(mPw, "Failed enableUsbData: opId:" + transactionId
+ " portId=" + portName , e);
try {
@@ -346,9 +362,8 @@ public final class UsbPortHidl implements UsbPortHal {
}
}
if (success) {
sUsbDataEnabled = enable;
sUsbDataStatus = enable ? USB_DATA_STATUS_UNKNOWN : USB_DATA_STATUS_DISABLED_FORCE;
}
try {
callback.onOperationComplete(success
? USB_OPERATION_SUCCESS
@@ -393,8 +408,8 @@ public final class UsbPortHidl implements UsbPortHal {
current.canChangePowerRole,
current.currentDataRole, current.canChangeDataRole,
false, CONTAMINANT_PROTECTION_NONE,
false, CONTAMINANT_DETECTION_NOT_SUPPORTED, sUsbDataEnabled,
false);
false, CONTAMINANT_DETECTION_NOT_SUPPORTED, new int[sUsbDataStatus],
false, POWER_BRICK_STATUS_UNKNOWN);
newPortInfo.add(temp);
UsbPortManager.logAndPrint(Log.INFO, mPw, "ClientCallback V1_0: "
+ current.portName);
@@ -427,8 +442,8 @@ public final class UsbPortHidl implements UsbPortHal {
current.status.canChangePowerRole,
current.status.currentDataRole, current.status.canChangeDataRole,
false, CONTAMINANT_PROTECTION_NONE,
false, CONTAMINANT_DETECTION_NOT_SUPPORTED, sUsbDataEnabled,
false);
false, CONTAMINANT_DETECTION_NOT_SUPPORTED, new int[sUsbDataStatus],
false, POWER_BRICK_STATUS_UNKNOWN);
newPortInfo.add(temp);
UsbPortManager.logAndPrint(Log.INFO, mPw, "ClientCallback V1_1: "
+ current.status.portName);
@@ -465,8 +480,8 @@ public final class UsbPortHidl implements UsbPortHal {
current.contaminantProtectionStatus,
current.supportsEnableContaminantPresenceDetection,
current.contaminantDetectionStatus,
sUsbDataEnabled,
false);
new int[sUsbDataStatus],
false, POWER_BRICK_STATUS_UNKNOWN);
newPortInfo.add(temp);
UsbPortManager.logAndPrint(Log.INFO, mPw, "ClientCallback V1_2: "
+ current.status_1_1.status.portName);