From bdbb2d15beeda11b94d1008061dd071902067b0b Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Fri, 26 Nov 2021 22:56:17 -0800 Subject: [PATCH] Add limitPowerTransfer SystemAPI to UsbPort limitPowerTransfer when invoked limits the power transfer in and out of the USB port. limitPortTransfer is deactivated either when limitPowerTransfer is deactivated by the caller or when Usb disconnect event is observed by the Usb service. limitPowerTransfer API is asynchronous and the caller gets notified by invoking UsbOperationCallback.onUsbOperationComplete callback to notify the status of the operation. USB_PORT_CHANGED event is broadcasted when port's limitPowerTransfer status changes. UsbPortStatus object is updated to reflect the current status. limitPowerTransfer is protected by android.permission.MANAGE_USB. Bug: 199357330 Bug: 199358576 Test: Tested on Pixel CTS-Coverage-Bug: 215019881 Signed-off-by: Badhri Jagan Sridharan Change-Id: Iabd1187f7527e14ed758abe355ba94a511d8c859 --- core/api/system-current.txt | 7 ++ .../android/hardware/usb/IUsbManager.aidl | 5 ++ .../java/android/hardware/usb/UsbManager.java | 32 +++++++ core/java/android/hardware/usb/UsbPort.java | 77 +++++++++++++++++ .../android/hardware/usb/UsbPortStatus.java | 22 ++++- .../com/android/internal/usb/DumpUtils.java | 2 + core/proto/android/service/usb.proto | 1 + .../android/server/usb/UsbPortManager.java | 83 +++++++++++++++++-- .../com/android/server/usb/UsbService.java | 22 +++++ .../server/usb/hal/port/RawPortInfo.java | 11 ++- .../server/usb/hal/port/UsbPortAidl.java | 69 ++++++++++++++- .../server/usb/hal/port/UsbPortHal.java | 15 ++++ .../server/usb/hal/port/UsbPortHidl.java | 20 ++++- 13 files changed, 350 insertions(+), 16 deletions(-) diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 0c8ee6e07c2fb..7a76f7a3b5348 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -4686,9 +4686,15 @@ 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 @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 + field public static final int ENABLE_LIMIT_POWER_TRANSFER_ERROR_NOT_SUPPORTED = 2; // 0x2 + field public static final int ENABLE_LIMIT_POWER_TRANSFER_ERROR_OTHER = 4; // 0x4 + field public static final int ENABLE_LIMIT_POWER_TRANSFER_ERROR_PORT_MISMATCH = 3; // 0x3 + field public static final int ENABLE_LIMIT_POWER_TRANSFER_SUCCESS = 0; // 0x0 field public static final int ENABLE_USB_DATA_ERROR_INTERNAL = 1; // 0x1 field public static final int ENABLE_USB_DATA_ERROR_NOT_SUPPORTED = 2; // 0x2 field public static final int ENABLE_USB_DATA_ERROR_OTHER = 4; // 0x4 @@ -4703,6 +4709,7 @@ package android.hardware.usb { method public int getCurrentPowerRole(); method public int getSupportedRoleCombinations(); method public boolean isConnected(); + method public boolean isPowerTransferLimited(); method public boolean isRoleCombinationSupported(int, int); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator CREATOR; diff --git a/core/java/android/hardware/usb/IUsbManager.aidl b/core/java/android/hardware/usb/IUsbManager.aidl index 3e79f18a4d65f..1d0837e3f1188 100644 --- a/core/java/android/hardware/usb/IUsbManager.aidl +++ b/core/java/android/hardware/usb/IUsbManager.aidl @@ -157,6 +157,11 @@ interface IUsbManager /* Sets the port's current role. */ void setPortRoles(in String portId, int powerRole, int dataRole); + /* Limit power transfer in & out of the port within the allowed limit by the USB + * specification. + */ + void enableLimitPowerTransfer(in String portId, boolean limit, int operationId, in IUsbOperationInternal callback); + /* Enable/disable contaminant detection */ void enableContaminantDetection(in String portId, boolean enable); diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java index 03eb461cc8d33..eb3e84d27c7c8 100644 --- a/core/java/android/hardware/usb/UsbManager.java +++ b/core/java/android/hardware/usb/UsbManager.java @@ -1291,6 +1291,38 @@ public class UsbManager { } } + /** + * Should only be called by {@link UsbPort#enableLimitPowerTransfer}. + *

+ * limits or restores power transfer in and out of USB port. + * + * @param port USB port for which power transfer has to be limited or restored. + * @param limit limit power transfer when true. + * relax power transfer restrictions when false. + * @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 enableLimitPowerTransfer(@NonNull UsbPort port, boolean limit, int operationId, + IUsbOperationInternal callback) { + Objects.requireNonNull(port, "enableLimitPowerTransfer:port must not be null. opId:" + + operationId); + try { + mService.enableLimitPowerTransfer(port.getId(), limit, operationId, callback); + } catch (RemoteException e) { + Log.e(TAG, "enableLimitPowerTransfer failed. opId:" + operationId, e); + try { + callback.onOperationComplete(UsbOperationInternal.USB_OPERATION_ERROR_INTERNAL); + } catch (RemoteException r) { + Log.e(TAG, "enableLimitPowerTransfer failed to call onOperationComplete. opId:" + + operationId, r); + } + throw e.rethrowFromSystemServer(); + } + } + /** * Should only be called by {@link UsbPort#enableUsbData}. *

diff --git a/core/java/android/hardware/usb/UsbPort.java b/core/java/android/hardware/usb/UsbPort.java index f469a3e66d507..e908c245fc8eb 100644 --- a/core/java/android/hardware/usb/UsbPort.java +++ b/core/java/android/hardware/usb/UsbPort.java @@ -118,6 +118,42 @@ public final class UsbPort { @Retention(RetentionPolicy.SOURCE) @interface EnableUsbDataStatus{} + /** + * The {@link #enableLimitPowerTransfer} request was successfully completed. + */ + public static final int ENABLE_LIMIT_POWER_TRANSFER_SUCCESS = 0; + + /** + * The {@link #enableLimitPowerTransfer} request failed due to internal error. + */ + public static final int ENABLE_LIMIT_POWER_TRANSFER_ERROR_INTERNAL = 1; + + /** + * The {@link #enableLimitPowerTransfer} request failed as it's not supported. + */ + public static final int ENABLE_LIMIT_POWER_TRANSFER_ERROR_NOT_SUPPORTED = 2; + + /** + * The {@link #enableLimitPowerTransfer} request failed as port id mismatched. + */ + public static final int ENABLE_LIMIT_POWER_TRANSFER_ERROR_PORT_MISMATCH = 3; + + /** + * The {@link #enableLimitPowerTransfer} request failed due to other reasons. + */ + public static final int ENABLE_LIMIT_POWER_TRANSFER_ERROR_OTHER = 4; + + /** @hide */ + @IntDef(prefix = { "ENABLE_LIMIT_POWER_TRANSFER_" }, value = { + ENABLE_LIMIT_POWER_TRANSFER_SUCCESS, + ENABLE_LIMIT_POWER_TRANSFER_ERROR_INTERNAL, + ENABLE_LIMIT_POWER_TRANSFER_ERROR_NOT_SUPPORTED, + ENABLE_LIMIT_POWER_TRANSFER_ERROR_PORT_MISMATCH, + ENABLE_LIMIT_POWER_TRANSFER_ERROR_OTHER + }) + @Retention(RetentionPolicy.SOURCE) + @interface EnableLimitPowerTransferStatus{} + /** @hide */ public UsbPort(@NonNull UsbManager usbManager, @NonNull String id, int supportedModes, int supportedContaminantProtectionModes, @@ -271,6 +307,47 @@ public final class UsbPort { } } + /** + * Limits power transfer In and out of the port. + *

+ * Disables charging and limits sourcing power(when permitted by the USB spec) until + * port disconnect event. + *

+ * @param enable limits power transfer when true. + * @return {@link #ENABLE_LIMIT_POWER_TRANSFER_SUCCESS} when request completes successfully or + * {@link #ENABLE_LIMIT_POWER_TRANSFER_ERROR_INTERNAL} when request fails due to + * internal error or + * {@link ENABLE_LIMIT_POWER_TRANSFER_ERROR_NOT_SUPPORTED} when not supported or + * {@link ENABLE_LIMIT_POWER_TRANSFER_ERROR_PORT_MISMATCH} when request fails due to + * port id mismatch or + * {@link ENABLE_LIMIT_POWER_TRANSFER_ERROR_OTHER} when fails due to other reasons. + */ + @CheckResult + @RequiresPermission(Manifest.permission.MANAGE_USB) + public @EnableLimitPowerTransferStatus int enableLimitPowerTransfer(boolean enable) { + // UID is added To minimize operationID overlap between two different packages. + int operationId = sUsbOperationCount.incrementAndGet() + Binder.getCallingUid(); + Log.i(TAG, "enableLimitPowerTransfer opId:" + operationId + + " callingUid:" + Binder.getCallingUid()); + UsbOperationInternal opCallback = + new UsbOperationInternal(operationId, mId); + mUsbManager.enableLimitPowerTransfer(this, enable, operationId, opCallback); + opCallback.waitForOperationComplete(); + int result = opCallback.getStatus(); + switch (result) { + case USB_OPERATION_SUCCESS: + return ENABLE_LIMIT_POWER_TRANSFER_SUCCESS; + case USB_OPERATION_ERROR_INTERNAL: + return ENABLE_LIMIT_POWER_TRANSFER_ERROR_INTERNAL; + case USB_OPERATION_ERROR_NOT_SUPPORTED: + return ENABLE_LIMIT_POWER_TRANSFER_ERROR_NOT_SUPPORTED; + case USB_OPERATION_ERROR_PORT_MISMATCH: + return ENABLE_LIMIT_POWER_TRANSFER_ERROR_PORT_MISMATCH; + default: + return ENABLE_LIMIT_POWER_TRANSFER_ERROR_OTHER; + } + } + /** * @hide **/ diff --git a/core/java/android/hardware/usb/UsbPortStatus.java b/core/java/android/hardware/usb/UsbPortStatus.java index bd2f9aa55bdaa..934c5067c11fa 100644 --- a/core/java/android/hardware/usb/UsbPortStatus.java +++ b/core/java/android/hardware/usb/UsbPortStatus.java @@ -43,6 +43,7 @@ public final class UsbPortStatus implements Parcelable { private final @ContaminantProtectionStatus int mContaminantProtectionStatus; private final @ContaminantDetectionStatus int mContaminantDetectionStatus; private final boolean mUsbDataEnabled; + private final boolean mPowerTransferLimited; /** * Power role: This USB port does not have a power role. @@ -224,7 +225,8 @@ public final class UsbPortStatus implements Parcelable { /** @hide */ public UsbPortStatus(int currentMode, int currentPowerRole, int currentDataRole, int supportedRoleCombinations, int contaminantProtectionStatus, - int contaminantDetectionStatus, boolean usbDataEnabled) { + int contaminantDetectionStatus, boolean usbDataEnabled, + boolean powerTransferLimited) { mCurrentMode = currentMode; mCurrentPowerRole = currentPowerRole; mCurrentDataRole = currentDataRole; @@ -232,6 +234,7 @@ public final class UsbPortStatus implements Parcelable { mContaminantProtectionStatus = contaminantProtectionStatus; mContaminantDetectionStatus = contaminantDetectionStatus; mUsbDataEnabled = usbDataEnabled; + mPowerTransferLimited = powerTransferLimited; } /** @hide */ @@ -245,6 +248,7 @@ public final class UsbPortStatus implements Parcelable { mContaminantProtectionStatus = contaminantProtectionStatus; mContaminantDetectionStatus = contaminantDetectionStatus; mUsbDataEnabled = true; + mPowerTransferLimited = false; } /** @@ -336,6 +340,16 @@ public final class UsbPortStatus implements Parcelable { return mUsbDataEnabled; } + /** + * Returns whether power transfer is limited. + * + * @return true when power transfer is limited. + * false otherwise. + */ + public boolean isPowerTransferLimited() { + return mPowerTransferLimited; + } + @NonNull @Override public String toString() { @@ -351,6 +365,8 @@ public final class UsbPortStatus implements Parcelable { + getContaminantProtectionStatus() + ", usbDataEnabled=" + getUsbDataStatus() + + ", isPowerTransferLimited=" + + isPowerTransferLimited() + "}"; } @@ -368,6 +384,7 @@ public final class UsbPortStatus implements Parcelable { dest.writeInt(mContaminantProtectionStatus); dest.writeInt(mContaminantDetectionStatus); dest.writeBoolean(mUsbDataEnabled); + dest.writeBoolean(mPowerTransferLimited); } public static final @NonNull Parcelable.Creator CREATOR = @@ -381,9 +398,10 @@ public final class UsbPortStatus implements Parcelable { int contaminantProtectionStatus = in.readInt(); int contaminantDetectionStatus = in.readInt(); boolean usbDataEnabled = in.readBoolean(); + boolean powerTransferLimited = in.readBoolean(); return new UsbPortStatus(currentMode, currentPowerRole, currentDataRole, supportedRoleCombinations, contaminantProtectionStatus, - contaminantDetectionStatus, usbDataEnabled); + contaminantDetectionStatus, usbDataEnabled, powerTransferLimited); } @Override diff --git a/core/java/com/android/internal/usb/DumpUtils.java b/core/java/com/android/internal/usb/DumpUtils.java index 744fe595d283b..b06a7f4e56e41 100644 --- a/core/java/com/android/internal/usb/DumpUtils.java +++ b/core/java/com/android/internal/usb/DumpUtils.java @@ -246,6 +246,8 @@ public class DumpUtils { status.getContaminantDetectionStatus()); dump.write("usb_data_enabled", UsbPortStatusProto.USB_DATA_ENABLED, status.getUsbDataStatus()); + dump.write("is_power_transfer_limited", UsbPortStatusProto.IS_POWER_TRANSFER_LIMITED, + status.isPowerTransferLimited()); dump.end(token); } } diff --git a/core/proto/android/service/usb.proto b/core/proto/android/service/usb.proto index c27aab551073a..b3f54f9afce23 100644 --- a/core/proto/android/service/usb.proto +++ b/core/proto/android/service/usb.proto @@ -265,6 +265,7 @@ message UsbPortStatusProto { repeated UsbPortStatusRoleCombinationProto role_combinations = 5; optional android.service.ContaminantPresenceStatus contaminant_presence_status = 6; optional bool usb_data_enabled = 7; + optional bool is_power_transfer_limited = 8; } message UsbPortStatusRoleCombinationProto { diff --git a/services/usb/java/com/android/server/usb/UsbPortManager.java b/services/usb/java/com/android/server/usb/UsbPortManager.java index d4726397b5eb5..98173adfd0eb4 100644 --- a/services/usb/java/com/android/server/usb/UsbPortManager.java +++ b/services/usb/java/com/android/server/usb/UsbPortManager.java @@ -226,6 +226,7 @@ public class UsbPortManager { intent.setComponent(ComponentName.unflattenFromString(r.getString( com.android.internal.R.string.config_usbContaminantActivity))); intent.putExtra(UsbManager.EXTRA_PORT, ParcelableUsbPort.of(currentPortInfo.mUsbPort)); + intent.putExtra(UsbManager.EXTRA_PORT_STATUS, currentPortInfo.mUsbPortStatus); // Simple notification clicks are immutable PendingIntent pi = PendingIntent.getActivityAsUser(mContext, 0, @@ -339,6 +340,49 @@ public class UsbPortManager { } } + /** + * Limits power transfer in/out of USB-C port. + * + * @param portId port identifier. + * @param limit limit power transfer when true. + */ + public void enableLimitPowerTransfer(@NonNull String portId, boolean limit, long transactionId, + IUsbOperationInternal callback, IndentingPrintWriter pw) { + Objects.requireNonNull(portId); + final PortInfo portInfo = mPorts.get(portId); + if (portInfo == null) { + logAndPrint(Log.ERROR, pw, "enableLimitPowerTransfer: No such port: " + portId + + " opId:" + transactionId); + try { + if (callback != null) { + callback.onOperationComplete(USB_OPERATION_ERROR_PORT_MISMATCH); + } + } catch (RemoteException e) { + logAndPrintException(pw, + "enableLimitPowerTransfer: Failed to call OperationComplete. opId:" + + transactionId, e); + } + return; + } + + try { + try { + mUsbPortHal.enableLimitPowerTransfer(portId, limit, transactionId, callback); + } catch (Exception e) { + logAndPrintException(pw, + "enableLimitPowerTransfer: Failed to limit power transfer. opId:" + + transactionId , e); + if (callback != null) { + callback.onOperationComplete(USB_OPERATION_ERROR_INTERNAL); + } + } + } catch (RemoteException e) { + logAndPrintException(pw, + "enableLimitPowerTransfer:Failed to call onOperationComplete. opId:" + + transactionId, e); + } + } + /** * Enable/disable the USB data signaling * @@ -715,7 +759,8 @@ public class UsbPortManager { portInfo.contaminantProtectionStatus, portInfo.supportsEnableContaminantPresenceDetection, portInfo.contaminantDetectionStatus, - portInfo.usbDataEnabled, pw); + portInfo.usbDataEnabled, + portInfo.powerTransferLimited, pw); } } else { for (RawPortInfo currentPortInfo : newPortInfo) { @@ -728,7 +773,8 @@ public class UsbPortManager { currentPortInfo.contaminantProtectionStatus, currentPortInfo.supportsEnableContaminantPresenceDetection, currentPortInfo.contaminantDetectionStatus, - currentPortInfo.usbDataEnabled, pw); + currentPortInfo.usbDataEnabled, + currentPortInfo.powerTransferLimited, pw); } } @@ -765,6 +811,7 @@ public class UsbPortManager { boolean supportsEnableContaminantPresenceDetection, int contaminantDetectionStatus, boolean usbDataEnabled, + boolean powerTransferLimited, IndentingPrintWriter pw) { // Only allow mode switch capability for dual role ports. // Validate that the current mode matches the supported modes we expect. @@ -823,7 +870,8 @@ public class UsbPortManager { currentPowerRole, canChangePowerRole, currentDataRole, canChangeDataRole, supportedRoleCombinations, contaminantProtectionStatus, - contaminantDetectionStatus, usbDataEnabled); + contaminantDetectionStatus, usbDataEnabled, + powerTransferLimited); mPorts.put(portId, portInfo); } else { // Validate that ports aren't changing definition out from under us. @@ -860,7 +908,8 @@ public class UsbPortManager { currentPowerRole, canChangePowerRole, currentDataRole, canChangeDataRole, supportedRoleCombinations, contaminantProtectionStatus, - contaminantDetectionStatus, usbDataEnabled)) { + contaminantDetectionStatus, usbDataEnabled, + powerTransferLimited)) { portInfo.mDisposition = PortInfo.DISPOSITION_CHANGED; } else { portInfo.mDisposition = PortInfo.DISPOSITION_READY; @@ -882,6 +931,7 @@ public class UsbPortManager { private void handlePortChangedLocked(PortInfo portInfo, IndentingPrintWriter pw) { logAndPrint(Log.INFO, pw, "USB port changed: " + portInfo); enableContaminantDetectionIfNeeded(portInfo, pw); + disableLimitPowerTransferIfNeeded(portInfo, pw); handlePortLocked(portInfo, pw); } @@ -938,6 +988,19 @@ public class UsbPortManager { } } + private void disableLimitPowerTransferIfNeeded(PortInfo portInfo, IndentingPrintWriter pw) { + if (!mConnected.containsKey(portInfo.mUsbPort.getId())) { + return; + } + + if (mConnected.get(portInfo.mUsbPort.getId()) + && !portInfo.mUsbPortStatus.isConnected() + && portInfo.mUsbPortStatus.isPowerTransferLimited()) { + // Relax enableLimitPowerTransfer upon unplug. + enableLimitPowerTransfer(portInfo.mUsbPort.getId(), false, ++mTransactionId, null, pw); + } + } + private void logToStatsd(PortInfo portInfo, IndentingPrintWriter pw) { // Port is removed if (portInfo.mUsbPortStatus == null) { @@ -1072,7 +1135,7 @@ public class UsbPortManager { != supportedRoleCombinations) { mUsbPortStatus = new UsbPortStatus(currentMode, currentPowerRole, currentDataRole, supportedRoleCombinations, UsbPortStatus.CONTAMINANT_PROTECTION_NONE, - UsbPortStatus.CONTAMINANT_DETECTION_NOT_SUPPORTED, true); + UsbPortStatus.CONTAMINANT_DETECTION_NOT_SUPPORTED, true, false); dispositionChanged = true; } @@ -1091,7 +1154,8 @@ public class UsbPortManager { int currentPowerRole, boolean canChangePowerRole, int currentDataRole, boolean canChangeDataRole, int supportedRoleCombinations, int contaminantProtectionStatus, - int contaminantDetectionStatus, boolean usbDataEnabled) { + int contaminantDetectionStatus, boolean usbDataEnabled, + boolean powerTransferLimited) { boolean dispositionChanged = false; mCanChangeMode = canChangeMode; @@ -1108,10 +1172,13 @@ public class UsbPortManager { || mUsbPortStatus.getContaminantDetectionStatus() != contaminantDetectionStatus || mUsbPortStatus.getUsbDataStatus() - != usbDataEnabled){ + != usbDataEnabled + || mUsbPortStatus.isPowerTransferLimited() + != powerTransferLimited) { mUsbPortStatus = new UsbPortStatus(currentMode, currentPowerRole, currentDataRole, supportedRoleCombinations, contaminantProtectionStatus, - contaminantDetectionStatus, usbDataEnabled); + contaminantDetectionStatus, usbDataEnabled, + powerTransferLimited); dispositionChanged = true; } diff --git a/services/usb/java/com/android/server/usb/UsbService.java b/services/usb/java/com/android/server/usb/UsbService.java index 28227fcf04684..51643e7d7d3c7 100644 --- a/services/usb/java/com/android/server/usb/UsbService.java +++ b/services/usb/java/com/android/server/usb/UsbService.java @@ -733,6 +733,28 @@ public class UsbService extends IUsbManager.Stub { } } + @Override + public void enableLimitPowerTransfer(String portId, boolean limit, int operationId, + IUsbOperationInternal callback) { + Objects.requireNonNull(portId, "portId must not be null. opID:" + operationId); + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null); + + final long ident = Binder.clearCallingIdentity(); + try { + if (mPortManager != null) { + mPortManager.enableLimitPowerTransfer(portId, limit, operationId, callback, null); + } else { + try { + callback.onOperationComplete(USB_OPERATION_ERROR_INTERNAL); + } catch (RemoteException e) { + Slog.e(TAG, "enableLimitPowerTransfer: Failed to call onOperationComplete", e); + } + } + } finally { + Binder.restoreCallingIdentity(ident); + } + } + @Override public void enableContaminantDetection(String portId, boolean enable) { Objects.requireNonNull(portId, "portId must not be null"); diff --git a/services/usb/java/com/android/server/usb/hal/port/RawPortInfo.java b/services/usb/java/com/android/server/usb/hal/port/RawPortInfo.java index 9c6cbbd96460d..8dfc85995272c 100644 --- a/services/usb/java/com/android/server/usb/hal/port/RawPortInfo.java +++ b/services/usb/java/com/android/server/usb/hal/port/RawPortInfo.java @@ -38,6 +38,7 @@ public final class RawPortInfo implements Parcelable { public boolean supportsEnableContaminantPresenceDetection; public int contaminantDetectionStatus; public boolean usbDataEnabled; + public boolean powerTransferLimited; public RawPortInfo(String portId, int supportedModes) { this.portId = portId; @@ -48,6 +49,7 @@ public final class RawPortInfo implements Parcelable { this.supportsEnableContaminantPresenceDetection = false; this.contaminantDetectionStatus = UsbPortStatus.CONTAMINANT_DETECTION_NOT_SUPPORTED; this.usbDataEnabled = true; + this.powerTransferLimited = false; } public RawPortInfo(String portId, int supportedModes, int supportedContaminantProtectionModes, @@ -58,7 +60,8 @@ public final class RawPortInfo implements Parcelable { int contaminantProtectionStatus, boolean supportsEnableContaminantPresenceDetection, int contaminantDetectionStatus, - boolean usbDataEnabled) { + boolean usbDataEnabled, + boolean powerTransferLimited) { this.portId = portId; this.supportedModes = supportedModes; this.supportedContaminantProtectionModes = supportedContaminantProtectionModes; @@ -75,6 +78,7 @@ public final class RawPortInfo implements Parcelable { supportsEnableContaminantPresenceDetection; this.contaminantDetectionStatus = contaminantDetectionStatus; this.usbDataEnabled = usbDataEnabled; + this.powerTransferLimited = powerTransferLimited; } @Override @@ -98,6 +102,7 @@ public final class RawPortInfo implements Parcelable { dest.writeBoolean(supportsEnableContaminantPresenceDetection); dest.writeInt(contaminantDetectionStatus); dest.writeBoolean(usbDataEnabled); + dest.writeBoolean(powerTransferLimited); } public static final Parcelable.Creator CREATOR = @@ -118,6 +123,7 @@ public final class RawPortInfo implements Parcelable { boolean supportsEnableContaminantPresenceDetection = in.readBoolean(); int contaminantDetectionStatus = in.readInt(); boolean usbDataEnabled = in.readBoolean(); + boolean powerTransferLimited = in.readBoolean(); return new RawPortInfo(id, supportedModes, supportedContaminantProtectionModes, currentMode, canChangeMode, currentPowerRole, canChangePowerRole, @@ -125,7 +131,8 @@ public final class RawPortInfo implements Parcelable { supportsEnableContaminantPresenceProtection, contaminantProtectionStatus, supportsEnableContaminantPresenceDetection, - contaminantDetectionStatus, usbDataEnabled); + contaminantDetectionStatus, usbDataEnabled, + powerTransferLimited); } @Override diff --git a/services/usb/java/com/android/server/usb/hal/port/UsbPortAidl.java b/services/usb/java/com/android/server/usb/hal/port/UsbPortAidl.java index 6f91a2a562806..24602426930d6 100644 --- a/services/usb/java/com/android/server/usb/hal/port/UsbPortAidl.java +++ b/services/usb/java/com/android/server/usb/hal/port/UsbPortAidl.java @@ -281,6 +281,47 @@ public final class UsbPortAidl implements UsbPortHal { } } + @Override + public void enableLimitPowerTransfer(String portName, boolean limit, long operationID, + IUsbOperationInternal callback) { + Objects.requireNonNull(portName); + long key = operationID; + synchronized (mLock) { + try { + if (mProxy == null) { + logAndPrint(Log.ERROR, mPw, + "enableLimitPowerTransfer: 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, "enableUsbData: operationID exists ! opID:" + + operationID + " key:" + key); + } + try { + sCallbacks.put(key, callback); + mProxy.limitPowerTransfer(portName, limit, key); + } catch (RemoteException e) { + logAndPrintException(mPw, + "enableLimitPowerTransfer: Failed while invoking AIDL HAL" + + " portID=" + portName + " opID:" + operationID, e); + if (callback != null) { + callback.onOperationComplete(USB_OPERATION_ERROR_INTERNAL); + } + sCallbacks.remove(key); + } + } catch (RemoteException e) { + logAndPrintException(mPw, + "enableLimitPowerTransfer: Failed to call onOperationComplete portID=" + + portName + " opID:" + operationID, e); + } + } + } + private static class HALCallback extends IUsbCallback.Stub { public IndentingPrintWriter mPw; public UsbPortManager mPortManager; @@ -393,7 +434,8 @@ public final class UsbPortAidl implements UsbPortHal { toContaminantProtectionStatus(current.contaminantProtectionStatus), current.supportsEnableContaminantPresenceDetection, current.contaminantDetectionStatus, - current.usbDataEnabled); + current.usbDataEnabled, + current.powerTransferLimited); newPortInfo.add(temp); UsbPortManager.logAndPrint(Log.INFO, mPw, "ClientCallback AIDL V1: " + current.portName); @@ -462,5 +504,30 @@ public final class UsbPortAidl implements UsbPortHal { + operationID + " failed. err:" + retval); } } + + @Override + public void notifyLimitPowerTransferStatus(String portName, boolean limit, int retval, + long operationID) { + if (retval == Status.SUCCESS) { + UsbPortManager.logAndPrint(Log.INFO, mPw, portName + ": opID:" + + operationID + " successful"); + } else { + UsbPortManager.logAndPrint(Log.ERROR, mPw, portName + + "notifyLimitPowerTransferStatus: 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, + "enableLimitPowerTransfer: Failed to call onOperationComplete", + e); + } + } } } diff --git a/services/usb/java/com/android/server/usb/hal/port/UsbPortHal.java b/services/usb/java/com/android/server/usb/hal/port/UsbPortHal.java index e7f9bc2fe7c31..90c89090ef164 100644 --- a/services/usb/java/com/android/server/usb/hal/port/UsbPortHal.java +++ b/services/usb/java/com/android/server/usb/hal/port/UsbPortHal.java @@ -166,4 +166,19 @@ public interface UsbPortHal { */ public boolean enableUsbData(String portName, boolean enable, long transactionId, IUsbOperationInternal callback); + + /** + * Invoked to enableLimitPowerTransfer on the specified port. + * + * @param portName Port Identifier. + * @param limit limit power transfer when true. Port wouldn't charge or power USB accessoried + * when set. + * Lift power transfer restrictions when false. + * @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 enableLimitPowerTransfer(String portName, boolean limit, long transactionId, + IUsbOperationInternal callback); } diff --git a/services/usb/java/com/android/server/usb/hal/port/UsbPortHidl.java b/services/usb/java/com/android/server/usb/hal/port/UsbPortHidl.java index 00d0d06705fcc..8a0370ad49090 100644 --- a/services/usb/java/com/android/server/usb/hal/port/UsbPortHidl.java +++ b/services/usb/java/com/android/server/usb/hal/port/UsbPortHidl.java @@ -270,6 +270,17 @@ public final class UsbPortHidl implements UsbPortHal { } } + @Override + public void enableLimitPowerTransfer(String portName, boolean limit, 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) { @@ -382,7 +393,8 @@ public final class UsbPortHidl implements UsbPortHal { current.canChangePowerRole, current.currentDataRole, current.canChangeDataRole, false, CONTAMINANT_PROTECTION_NONE, - false, CONTAMINANT_DETECTION_NOT_SUPPORTED, sUsbDataEnabled); + false, CONTAMINANT_DETECTION_NOT_SUPPORTED, sUsbDataEnabled, + false); newPortInfo.add(temp); UsbPortManager.logAndPrint(Log.INFO, mPw, "ClientCallback V1_0: " + current.portName); @@ -415,7 +427,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, CONTAMINANT_DETECTION_NOT_SUPPORTED, sUsbDataEnabled, + false); newPortInfo.add(temp); UsbPortManager.logAndPrint(Log.INFO, mPw, "ClientCallback V1_1: " + current.status.portName); @@ -452,7 +465,8 @@ public final class UsbPortHidl implements UsbPortHal { current.contaminantProtectionStatus, current.supportsEnableContaminantPresenceDetection, current.contaminantDetectionStatus, - sUsbDataEnabled); + sUsbDataEnabled, + false); newPortInfo.add(temp); UsbPortManager.logAndPrint(Log.INFO, mPw, "ClientCallback V1_2: " + current.status_1_1.status.portName);