diff options
18 files changed, 1825 insertions, 441 deletions
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt index 70bb13a36b6e..25ea0b0274a4 100644 --- a/core/api/module-lib-current.txt +++ b/core/api/module-lib-current.txt @@ -113,10 +113,12 @@ package android.hardware.usb { field public static final int USB_DATA_TRANSFER_RATE_LOW_SPEED = 2; // 0x2 field public static final int USB_DATA_TRANSFER_RATE_UNKNOWN = -1; // 0xffffffff field public static final int USB_HAL_NOT_SUPPORTED = -1; // 0xffffffff + field public static final int USB_HAL_RETRY = -2; // 0xfffffffe field public static final int USB_HAL_V1_0 = 10; // 0xa field public static final int USB_HAL_V1_1 = 11; // 0xb field public static final int USB_HAL_V1_2 = 12; // 0xc field public static final int USB_HAL_V1_3 = 13; // 0xd + field public static final int USB_HAL_V2_0 = 20; // 0x14 } } diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 45fac2e09d5b..0c8ee6e07c2f 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -4686,8 +4686,14 @@ package android.hardware.usb { } public final class UsbPort { + 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_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 + field public static final int ENABLE_USB_DATA_ERROR_PORT_MISMATCH = 3; // 0x3 + field public static final int ENABLE_USB_DATA_SUCCESS = 0; // 0x0 } public final class UsbPortStatus implements android.os.Parcelable { diff --git a/core/java/android/hardware/usb/IUsbManager.aidl b/core/java/android/hardware/usb/IUsbManager.aidl index 7f07af79ab69..3e79f18a4d65 100644 --- a/core/java/android/hardware/usb/IUsbManager.aidl +++ b/core/java/android/hardware/usb/IUsbManager.aidl @@ -18,6 +18,7 @@ package android.hardware.usb; import android.app.PendingIntent; import android.content.ComponentName; +import android.hardware.usb.IUsbOperationInternal; import android.hardware.usb.UsbAccessory; import android.hardware.usb.UsbDevice; import android.hardware.usb.ParcelableUsbPort; @@ -136,7 +137,7 @@ interface IUsbManager void resetUsbGadget(); /* Set USB data on or off */ - boolean enableUsbDataSignal(boolean enable); + boolean enableUsbData(in String portId, boolean enable, int operationId, in IUsbOperationInternal callback); /* Gets the USB Hal Version. */ int getUsbHalVersion(); @@ -159,6 +160,6 @@ interface IUsbManager /* Enable/disable contaminant detection */ void enableContaminantDetection(in String portId, boolean enable); - /* Sets USB device connection handler. */ - void setUsbDeviceConnectionHandler(in ComponentName usbDeviceConnectionHandler); + /* Sets USB device connection handler. */ + void setUsbDeviceConnectionHandler(in ComponentName usbDeviceConnectionHandler); } diff --git a/core/java/android/hardware/usb/IUsbOperationInternal.aidl b/core/java/android/hardware/usb/IUsbOperationInternal.aidl new file mode 100644 index 000000000000..3f3bbf63ed8b --- /dev/null +++ b/core/java/android/hardware/usb/IUsbOperationInternal.aidl @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2021 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; + +/** + * @hide + */ +oneway interface IUsbOperationInternal { +void onOperationComplete(in int status); +} diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java index c29a948c2a26..03eb461cc8d3 100644 --- a/core/java/android/hardware/usb/UsbManager.java +++ b/core/java/android/hardware/usb/UsbManager.java @@ -36,6 +36,8 @@ import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.hardware.usb.gadget.V1_0.GadgetFunction; import android.hardware.usb.gadget.V1_2.UsbSpeed; +import android.hardware.usb.IUsbOperationInternal; +import android.hardware.usb.UsbPort; import android.os.Build; import android.os.Bundle; import android.os.ParcelFileDescriptor; @@ -48,6 +50,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.StringJoiner; /** @@ -517,6 +520,14 @@ public class UsbManager { public static final int USB_DATA_TRANSFER_RATE_40G = 40 * 1024; /** + * Returned when the client has to retry querying the version. + * + * @hide + */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + public static final int USB_HAL_RETRY = -2; + + /** * The Value for USB hal is not presented. * * {@hide} @@ -557,6 +568,14 @@ public class UsbManager { public static final int USB_HAL_V1_3 = 13; /** + * Value for USB Hal Version v2.0. + * + * @hide + */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + public static final int USB_HAL_V2_0 = 20; + + /** * Code for the charging usb function. Passed into {@link #setCurrentFunctions(long)} * {@hide} */ @@ -665,6 +684,7 @@ public class UsbManager { USB_HAL_V1_1, USB_HAL_V1_2, USB_HAL_V1_3, + USB_HAL_V2_0, }) public @interface UsbHalVersion {} @@ -1169,8 +1189,9 @@ public class UsbManager { /** * Enable/Disable the USB data signaling. * <p> - * Enables/Disables USB data path in all the USB ports. + * Enables/Disables USB data path of the first port.. * It will force to stop or restore USB data signaling. + * Call UsbPort API if the device has more than one UsbPort. * </p> * * @param enable enable or disable USB data signaling @@ -1181,11 +1202,11 @@ public class UsbManager { */ @RequiresPermission(Manifest.permission.MANAGE_USB) public boolean enableUsbDataSignal(boolean enable) { - try { - return mService.enableUsbDataSignal(enable); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); + List<UsbPort> usbPorts = getPorts(); + if (usbPorts.size() == 1) { + return usbPorts.get(0).enableUsbData(enable) == UsbPort.ENABLE_USB_DATA_SUCCESS; } + return false; } /** @@ -1271,6 +1292,41 @@ public class UsbManager { } /** + * Should only be called by {@link UsbPort#enableUsbData}. + * <p> + * Enables or disables USB data on the specific port. + * + * @param port USB port for which USB data needs to be enabled or disabled. + * @param enable Enable USB data when true. + * Disable USB data when false. + * @param operationId operationId for the request. + * @param callback callback object to be invoked when the operation is complete. + * @return True when the operation is asynchronous. The caller must therefore call + * {@link UsbOperationInternal#waitForOperationComplete} for processing + * the result. + * False when the operation is synchronous. Caller can proceed reading the result + * through {@link UsbOperationInternal#getStatus} + * @hide + */ + @RequiresPermission(Manifest.permission.MANAGE_USB) + boolean enableUsbData(@NonNull UsbPort port, boolean enable, int operationId, + IUsbOperationInternal callback) { + Objects.requireNonNull(port, "enableUsbData: port must not be null. opId:" + operationId); + try { + return mService.enableUsbData(port.getId(), enable, operationId, callback); + } catch (RemoteException e) { + Log.e(TAG, "enableUsbData: failed. opId:" + operationId, e); + try { + callback.onOperationComplete(UsbOperationInternal.USB_OPERATION_ERROR_INTERNAL); + } catch (RemoteException r) { + Log.e(TAG, "enableUsbData: failed to call onOperationComplete. opId:" + + operationId, r); + } + 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 diff --git a/core/java/android/hardware/usb/UsbOperationInternal.java b/core/java/android/hardware/usb/UsbOperationInternal.java new file mode 100644 index 000000000000..9bc2b3892a1e --- /dev/null +++ b/core/java/android/hardware/usb/UsbOperationInternal.java @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2022 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.annotation.IntDef; +import android.hardware.usb.IUsbOperationInternal; +import android.hardware.usb.UsbPort; +import android.util.Log; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.ReentrantLock; +import java.util.concurrent.TimeUnit; +/** + * UsbOperationInternal allows UsbPort to support both synchronous and + * asynchronous function irrespective of whether the underlying hal + * method is synchronous or asynchronous. + * + * @hide + */ +public final class UsbOperationInternal extends IUsbOperationInternal.Stub { + private static final String TAG = "UsbPortStatus"; + private final int mOperationID; + // Cached portId. + private final String mId; + // True implies operation did not timeout. + private boolean mOperationComplete; + private @UsbOperationStatus int mStatus; + final ReentrantLock mLock = new ReentrantLock(); + final Condition mOperationWait = mLock.newCondition(); + // Maximum time the caller has to wait for onOperationComplete to be called. + private static final int USB_OPERATION_TIMEOUT_MSECS = 5000; + + /** + * The requested operation was successfully completed. + * Returned in {@link onOperationComplete} and {@link getStatus}. + */ + public static final int USB_OPERATION_SUCCESS = 0; + + /** + * The requested operation failed due to internal error. + * Returned in {@link onOperationComplete} and {@link getStatus}. + */ + public static final int USB_OPERATION_ERROR_INTERNAL = 1; + + /** + * The requested operation failed as it's not supported. + * Returned in {@link onOperationComplete} and {@link getStatus}. + */ + public static final int USB_OPERATION_ERROR_NOT_SUPPORTED = 2; + + /** + * The requested operation failed as it's not supported. + * Returned in {@link onOperationComplete} and {@link getStatus}. + */ + public static final int USB_OPERATION_ERROR_PORT_MISMATCH = 3; + + @IntDef(prefix = { "USB_OPERATION_" }, value = { + USB_OPERATION_SUCCESS, + USB_OPERATION_ERROR_INTERNAL, + USB_OPERATION_ERROR_NOT_SUPPORTED, + USB_OPERATION_ERROR_PORT_MISMATCH + }) + @Retention(RetentionPolicy.SOURCE) + @interface UsbOperationStatus{} + + UsbOperationInternal(int operationID, String id) { + this.mOperationID = operationID; + this.mId = id; + } + + /** + * Hal glue layer would directly call this function when the requested + * operation is complete. + */ + @Override + public void onOperationComplete(@UsbOperationStatus int status) { + mLock.lock(); + try { + mOperationComplete = true; + mStatus = status; + Log.i(TAG, "Port:" + mId + " opID:" + mOperationID + " status:" + mStatus); + mOperationWait.signal(); + } finally { + mLock.unlock(); + } + } + + /** + * Caller invokes this function to wait for the operation to be complete. + */ + public void waitForOperationComplete() { + mLock.lock(); + try { + long now = System.currentTimeMillis(); + long deadline = now + USB_OPERATION_TIMEOUT_MSECS; + // Wait in loop to overcome spurious wakeups. + do { + mOperationWait.await(deadline - System.currentTimeMillis(), + TimeUnit.MILLISECONDS); + } while (!mOperationComplete && System.currentTimeMillis() < deadline); + if (!mOperationComplete) { + Log.e(TAG, "Port:" + mId + " opID:" + mOperationID + + " operationComplete not received in " + USB_OPERATION_TIMEOUT_MSECS + + "msecs"); + } + } catch (InterruptedException e) { + Log.e(TAG, "Port:" + mId + " opID:" + mOperationID + " operationComplete interrupted"); + } finally { + mLock.unlock(); + } + } + + public @UsbOperationStatus int getStatus() { + return mOperationComplete ? mStatus : USB_OPERATION_ERROR_INTERNAL; + } +} diff --git a/core/java/android/hardware/usb/UsbPort.java b/core/java/android/hardware/usb/UsbPort.java index 274e23fff292..f469a3e66d50 100644 --- a/core/java/android/hardware/usb/UsbPort.java +++ b/core/java/android/hardware/usb/UsbPort.java @@ -16,6 +16,10 @@ package android.hardware.usb; +import static android.hardware.usb.UsbOperationInternal.USB_OPERATION_ERROR_INTERNAL; +import static android.hardware.usb.UsbOperationInternal.USB_OPERATION_ERROR_NOT_SUPPORTED; +import static android.hardware.usb.UsbOperationInternal.USB_OPERATION_ERROR_PORT_MISMATCH; +import static android.hardware.usb.UsbOperationInternal.USB_OPERATION_SUCCESS; import static android.hardware.usb.UsbPortStatus.CONTAMINANT_DETECTION_DETECTED; import static android.hardware.usb.UsbPortStatus.CONTAMINANT_DETECTION_DISABLED; import static android.hardware.usb.UsbPortStatus.CONTAMINANT_DETECTION_NOT_DETECTED; @@ -34,15 +38,23 @@ import static android.hardware.usb.UsbPortStatus.POWER_ROLE_SINK; import static android.hardware.usb.UsbPortStatus.POWER_ROLE_SOURCE; import android.Manifest; +import android.annotation.CheckResult; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; +import android.hardware.usb.UsbOperationInternal; import android.hardware.usb.V1_0.Constants; +import android.os.Binder; +import android.util.Log; import com.android.internal.util.Preconditions; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.Objects; +import java.util.concurrent.atomic.AtomicInteger; /** * Represents a physical USB port and describes its characteristics. @@ -51,6 +63,7 @@ import java.util.Objects; */ @SystemApi public final class UsbPort { + private static final String TAG = "UsbPort"; private final String mId; private final int mSupportedModes; private final UsbManager mUsbManager; @@ -64,6 +77,47 @@ public final class UsbPort { */ private static final int POWER_ROLE_OFFSET = Constants.PortPowerRole.NONE; + /** + * Counter for tracking UsbOperation operations. + */ + private static final AtomicInteger sUsbOperationCount = new AtomicInteger(); + + /** + * The {@link #enableUsbData} request was successfully completed. + */ + public static final int ENABLE_USB_DATA_SUCCESS = 0; + + /** + * The {@link #enableUsbData} request failed due to internal error. + */ + public static final int ENABLE_USB_DATA_ERROR_INTERNAL = 1; + + /** + * The {@link #enableUsbData} request failed as it's not supported. + */ + public static final int ENABLE_USB_DATA_ERROR_NOT_SUPPORTED = 2; + + /** + * The {@link #enableUsbData} request failed as port id mismatched. + */ + public static final int ENABLE_USB_DATA_ERROR_PORT_MISMATCH = 3; + + /** + * The {@link #enableUsbData} request failed due to other reasons. + */ + public static final int ENABLE_USB_DATA_ERROR_OTHER = 4; + + /** @hide */ + @IntDef(prefix = { "ENABLE_USB_DATA_" }, value = { + ENABLE_USB_DATA_SUCCESS, + ENABLE_USB_DATA_ERROR_INTERNAL, + ENABLE_USB_DATA_ERROR_NOT_SUPPORTED, + ENABLE_USB_DATA_ERROR_PORT_MISMATCH, + ENABLE_USB_DATA_ERROR_OTHER + }) + @Retention(RetentionPolicy.SOURCE) + @interface EnableUsbDataStatus{} + /** @hide */ public UsbPort(@NonNull UsbManager usbManager, @NonNull String id, int supportedModes, int supportedContaminantProtectionModes, @@ -157,7 +211,7 @@ public final class UsbPort { * {@link UsbPortStatus#isRoleCombinationSupported UsbPortStatus.isRoleCombinationSupported}. * </p><p> * Note: This function is asynchronous and may fail silently without applying - * the requested changes. If this function does cause a status change to occur then + * the operationed changes. If this function does cause a status change to occur then * a {@link UsbManager#ACTION_USB_PORT_CHANGED} broadcast will be sent. * </p> * @@ -177,6 +231,47 @@ public final class UsbPort { } /** + * Enables/Disables Usb data on the port. + * + * @param enable When true enables USB data if disabled. + * When false disables USB data if enabled. + * @return {@link #ENABLE_USB_DATA_SUCCESS} when request completes successfully or + * {@link #ENABLE_USB_DATA_ERROR_INTERNAL} when request fails due to internal + * error or + * {@link ENABLE_USB_DATA_ERROR_NOT_SUPPORTED} when not supported or + * {@link ENABLE_USB_DATA_ERROR_PORT_MISMATCH} when request fails due to port id + * mismatch or + * {@link ENABLE_USB_DATA_ERROR_OTHER} when fails due to other reasons. + */ + @CheckResult + @RequiresPermission(Manifest.permission.MANAGE_USB) + public @EnableUsbDataStatus int enableUsbData(boolean enable) { + // 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()); + UsbOperationInternal opCallback = + new UsbOperationInternal(operationId, mId); + if (mUsbManager.enableUsbData(this, enable, operationId, opCallback) == true) { + opCallback.waitForOperationComplete(); + } + + int result = opCallback.getStatus(); + switch (result) { + case USB_OPERATION_SUCCESS: + return ENABLE_USB_DATA_SUCCESS; + case USB_OPERATION_ERROR_INTERNAL: + return ENABLE_USB_DATA_ERROR_INTERNAL; + case USB_OPERATION_ERROR_NOT_SUPPORTED: + return ENABLE_USB_DATA_ERROR_NOT_SUPPORTED; + case USB_OPERATION_ERROR_PORT_MISMATCH: + return ENABLE_USB_DATA_ERROR_PORT_MISMATCH; + default: + return ENABLE_USB_DATA_ERROR_OTHER; + } + } + + /** * @hide **/ public void enableContaminantDetection(boolean enable) { diff --git a/core/java/android/hardware/usb/UsbPortStatus.java b/core/java/android/hardware/usb/UsbPortStatus.java index bb7aff651b3d..bd2f9aa55bda 100644 --- a/core/java/android/hardware/usb/UsbPortStatus.java +++ b/core/java/android/hardware/usb/UsbPortStatus.java @@ -19,7 +19,6 @@ package android.hardware.usb; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.SystemApi; -import android.hardware.usb.V1_0.Constants; import android.os.Parcel; import android.os.Parcelable; @@ -36,27 +35,29 @@ import java.lang.annotation.RetentionPolicy; @Immutable @SystemApi public final class UsbPortStatus implements Parcelable { + private static final String TAG = "UsbPortStatus"; private final int mCurrentMode; private final @UsbPowerRole int mCurrentPowerRole; private final @UsbDataRole int mCurrentDataRole; private final int mSupportedRoleCombinations; private final @ContaminantProtectionStatus int mContaminantProtectionStatus; private final @ContaminantDetectionStatus int mContaminantDetectionStatus; + private final boolean mUsbDataEnabled; /** * Power role: This USB port does not have a power role. */ - public static final int POWER_ROLE_NONE = Constants.PortPowerRole.NONE; + public static final int POWER_ROLE_NONE = 0; /** * Power role: This USB port can act as a source (provide power). */ - public static final int POWER_ROLE_SOURCE = Constants.PortPowerRole.SOURCE; + public static final int POWER_ROLE_SOURCE = 1; /** * Power role: This USB port can act as a sink (receive power). */ - public static final int POWER_ROLE_SINK = Constants.PortPowerRole.SINK; + public static final int POWER_ROLE_SINK = 2; @IntDef(prefix = { "POWER_ROLE_" }, value = { POWER_ROLE_NONE, @@ -69,17 +70,17 @@ public final class UsbPortStatus implements Parcelable { /** * Power role: This USB port does not have a data role. */ - public static final int DATA_ROLE_NONE = Constants.PortDataRole.NONE; + public static final int DATA_ROLE_NONE = 0; /** * Data role: This USB port can act as a host (access data services). */ - public static final int DATA_ROLE_HOST = Constants.PortDataRole.HOST; + public static final int DATA_ROLE_HOST = 1; /** * Data role: This USB port can act as a device (offer data services). */ - public static final int DATA_ROLE_DEVICE = Constants.PortDataRole.DEVICE; + public static final int DATA_ROLE_DEVICE = 2; @IntDef(prefix = { "DATA_ROLE_" }, value = { DATA_ROLE_NONE, @@ -92,23 +93,23 @@ public final class UsbPortStatus implements Parcelable { /** * There is currently nothing connected to this USB port. */ - public static final int MODE_NONE = Constants.PortMode.NONE; + public static final int MODE_NONE = 0; /** - * This USB port can act as a downstream facing port (host). + * This USB port can act as an upstream facing port (device). * - * <p> Implies that the port supports the {@link #POWER_ROLE_SOURCE} and - * {@link #DATA_ROLE_HOST} combination of roles (and possibly others as well). + * <p> Implies that the port supports the {@link #POWER_ROLE_SINK} and + * {@link #DATA_ROLE_DEVICE} combination of roles (and possibly others as well). */ - public static final int MODE_DFP = Constants.PortMode.DFP; + public static final int MODE_UFP = 1 << 0; /** - * This USB port can act as an upstream facing port (device). + * This USB port can act as a downstream facing port (host). * - * <p> Implies that the port supports the {@link #POWER_ROLE_SINK} and - * {@link #DATA_ROLE_DEVICE} combination of roles (and possibly others as well). + * <p> Implies that the port supports the {@link #POWER_ROLE_SOURCE} and + * {@link #DATA_ROLE_HOST} combination of roles (and possibly others as well). */ - public static final int MODE_UFP = Constants.PortMode.UFP; + public static final int MODE_DFP = 1 << 1; /** * This USB port can act either as an downstream facing port (host) or as @@ -120,87 +121,76 @@ public final class UsbPortStatus implements Parcelable { * * @hide */ - public static final int MODE_DUAL = Constants.PortMode.DRP; + public static final int MODE_DUAL = MODE_UFP | MODE_DFP; /** * This USB port can support USB Type-C Audio accessory. */ - public static final int MODE_AUDIO_ACCESSORY = - android.hardware.usb.V1_1.Constants.PortMode_1_1.AUDIO_ACCESSORY; + public static final int MODE_AUDIO_ACCESSORY = 1 << 2; /** * This USB port can support USB Type-C debug accessory. */ - public static final int MODE_DEBUG_ACCESSORY = - android.hardware.usb.V1_1.Constants.PortMode_1_1.DEBUG_ACCESSORY; + public static final int MODE_DEBUG_ACCESSORY = 1 << 3; /** * Contaminant presence detection not supported by the device. * @hide */ - public static final int CONTAMINANT_DETECTION_NOT_SUPPORTED = - android.hardware.usb.V1_2.Constants.ContaminantDetectionStatus.NOT_SUPPORTED; + public static final int CONTAMINANT_DETECTION_NOT_SUPPORTED = 0; /** * Contaminant presence detection supported but disabled. * @hide */ - public static final int CONTAMINANT_DETECTION_DISABLED = - android.hardware.usb.V1_2.Constants.ContaminantDetectionStatus.DISABLED; + public static final int CONTAMINANT_DETECTION_DISABLED = 1; /** * Contaminant presence enabled but not detected. * @hide */ - public static final int CONTAMINANT_DETECTION_NOT_DETECTED = - android.hardware.usb.V1_2.Constants.ContaminantDetectionStatus.NOT_DETECTED; + public static final int CONTAMINANT_DETECTION_NOT_DETECTED = 2; /** * Contaminant presence enabled and detected. * @hide */ - public static final int CONTAMINANT_DETECTION_DETECTED = - android.hardware.usb.V1_2.Constants.ContaminantDetectionStatus.DETECTED; + public static final int CONTAMINANT_DETECTION_DETECTED = 3; /** * Contaminant protection - No action performed upon detection of * contaminant presence. * @hide */ - public static final int CONTAMINANT_PROTECTION_NONE = - android.hardware.usb.V1_2.Constants.ContaminantProtectionStatus.NONE; + public static final int CONTAMINANT_PROTECTION_NONE = 0; /** * Contaminant protection - Port is forced to sink upon detection of * contaminant presence. * @hide */ - public static final int CONTAMINANT_PROTECTION_SINK = - android.hardware.usb.V1_2.Constants.ContaminantProtectionStatus.FORCE_SINK; + public static final int CONTAMINANT_PROTECTION_SINK = 1 << 0; /** * Contaminant protection - Port is forced to source upon detection of * contaminant presence. * @hide */ - public static final int CONTAMINANT_PROTECTION_SOURCE = - android.hardware.usb.V1_2.Constants.ContaminantProtectionStatus.FORCE_SOURCE; + public static final int CONTAMINANT_PROTECTION_SOURCE = 1 << 1; /** * Contaminant protection - Port is disabled upon detection of * contaminant presence. * @hide */ - public static final int CONTAMINANT_PROTECTION_FORCE_DISABLE = - android.hardware.usb.V1_2.Constants.ContaminantProtectionStatus.FORCE_DISABLE; + public static final int CONTAMINANT_PROTECTION_FORCE_DISABLE = 1 << 2; /** * Contaminant protection - Port is disabled upon detection of * contaminant presence. * @hide */ - public static final int CONTAMINANT_PROTECTION_DISABLED = - android.hardware.usb.V1_2.Constants.ContaminantProtectionStatus.DISABLED; + public static final int CONTAMINANT_PROTECTION_DISABLED = 1 << 3; @IntDef(prefix = { "CONTAMINANT_DETECTION_" }, value = { CONTAMINANT_DETECTION_NOT_SUPPORTED, @@ -234,6 +224,19 @@ public final class UsbPortStatus implements Parcelable { /** @hide */ public UsbPortStatus(int currentMode, int currentPowerRole, int currentDataRole, int supportedRoleCombinations, int contaminantProtectionStatus, + int contaminantDetectionStatus, boolean usbDataEnabled) { + mCurrentMode = currentMode; + mCurrentPowerRole = currentPowerRole; + mCurrentDataRole = currentDataRole; + mSupportedRoleCombinations = supportedRoleCombinations; + mContaminantProtectionStatus = contaminantProtectionStatus; + mContaminantDetectionStatus = contaminantDetectionStatus; + mUsbDataEnabled = usbDataEnabled; + } + + /** @hide */ + public UsbPortStatus(int currentMode, int currentPowerRole, int currentDataRole, + int supportedRoleCombinations, int contaminantProtectionStatus, int contaminantDetectionStatus) { mCurrentMode = currentMode; mCurrentPowerRole = currentPowerRole; @@ -241,6 +244,7 @@ public final class UsbPortStatus implements Parcelable { mSupportedRoleCombinations = supportedRoleCombinations; mContaminantProtectionStatus = contaminantProtectionStatus; mContaminantDetectionStatus = contaminantDetectionStatus; + mUsbDataEnabled = true; } /** @@ -323,6 +327,15 @@ public final class UsbPortStatus implements Parcelable { return mContaminantProtectionStatus; } + /** + * Returns UsbData status. + * + * @hide + */ + public boolean getUsbDataStatus() { + return mUsbDataEnabled; + } + @NonNull @Override public String toString() { @@ -336,6 +349,8 @@ public final class UsbPortStatus implements Parcelable { + getContaminantDetectionStatus() + ", contaminantProtectionStatus=" + getContaminantProtectionStatus() + + ", usbDataEnabled=" + + getUsbDataStatus() + "}"; } @@ -352,6 +367,7 @@ public final class UsbPortStatus implements Parcelable { dest.writeInt(mSupportedRoleCombinations); dest.writeInt(mContaminantProtectionStatus); dest.writeInt(mContaminantDetectionStatus); + dest.writeBoolean(mUsbDataEnabled); } public static final @NonNull Parcelable.Creator<UsbPortStatus> CREATOR = @@ -364,9 +380,10 @@ public final class UsbPortStatus implements Parcelable { int supportedRoleCombinations = in.readInt(); int contaminantProtectionStatus = in.readInt(); int contaminantDetectionStatus = in.readInt(); + boolean usbDataEnabled = in.readBoolean(); return new UsbPortStatus(currentMode, currentPowerRole, currentDataRole, supportedRoleCombinations, contaminantProtectionStatus, - contaminantDetectionStatus); + contaminantDetectionStatus, usbDataEnabled); } @Override diff --git a/core/java/com/android/internal/usb/DumpUtils.java b/core/java/com/android/internal/usb/DumpUtils.java index 32601368f2fb..744fe595d283 100644 --- a/core/java/com/android/internal/usb/DumpUtils.java +++ b/core/java/com/android/internal/usb/DumpUtils.java @@ -244,7 +244,8 @@ 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.end(token); } } diff --git a/core/proto/android/service/usb.proto b/core/proto/android/service/usb.proto index 97097ff91219..c27aab551073 100644 --- a/core/proto/android/service/usb.proto +++ b/core/proto/android/service/usb.proto @@ -196,9 +196,19 @@ message UsbIsHeadsetProto { message UsbPortManagerProto { option (android.msg_privacy).dest = DEST_AUTOMATIC; + enum HalVersion { + V_UNKNOWN = 0; + V1_0 = 10; + V1_1 = 11; + V1_2 = 12; + V1_3 = 13; + V2 = 20; + } + optional bool is_simulation_active = 1; repeated UsbPortInfoProto usb_ports = 2; optional bool enable_usb_data_signaling = 3; + optional HalVersion hal_version = 4; } message UsbPortInfoProto { @@ -254,6 +264,7 @@ 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; } message UsbPortStatusRoleCombinationProto { diff --git a/services/usb/Android.bp b/services/usb/Android.bp index 01feacd826c5..3b50fa43536c 100644 --- a/services/usb/Android.bp +++ b/services/usb/Android.bp @@ -29,6 +29,7 @@ java_library_static { "android.hardware.usb-V1.1-java", "android.hardware.usb-V1.2-java", "android.hardware.usb-V1.3-java", + "android.hardware.usb-V1-java", "android.hardware.usb.gadget-V1.0-java", "android.hardware.usb.gadget-V1.1-java", "android.hardware.usb.gadget-V1.2-java", diff --git a/services/usb/java/com/android/server/usb/UsbPortManager.java b/services/usb/java/com/android/server/usb/UsbPortManager.java index ec28040f82d8..d4726397b5eb 100644 --- a/services/usb/java/com/android/server/usb/UsbPortManager.java +++ b/services/usb/java/com/android/server/usb/UsbPortManager.java @@ -16,6 +16,8 @@ package com.android.server.usb; +import static android.hardware.usb.UsbOperationInternal.USB_OPERATION_ERROR_PORT_MISMATCH; +import static android.hardware.usb.UsbOperationInternal.USB_OPERATION_ERROR_INTERNAL; import static android.hardware.usb.UsbPortStatus.CONTAMINANT_DETECTION_NOT_SUPPORTED; import static android.hardware.usb.UsbPortStatus.CONTAMINANT_PROTECTION_NONE; import static android.hardware.usb.UsbPortStatus.DATA_ROLE_DEVICE; @@ -25,6 +27,12 @@ import static android.hardware.usb.UsbPortStatus.MODE_DUAL; import static android.hardware.usb.UsbPortStatus.MODE_UFP; import static android.hardware.usb.UsbPortStatus.POWER_ROLE_SINK; import static android.hardware.usb.UsbPortStatus.POWER_ROLE_SOURCE; +import static com.android.server.usb.hal.port.UsbPortHal.HAL_POWER_ROLE_SOURCE; +import static com.android.server.usb.hal.port.UsbPortHal.HAL_POWER_ROLE_SINK; +import static com.android.server.usb.hal.port.UsbPortHal.HAL_DATA_ROLE_HOST; +import static com.android.server.usb.hal.port.UsbPortHal.HAL_DATA_ROLE_DEVICE; +import static com.android.server.usb.hal.port.UsbPortHal.HAL_MODE_DFP; +import static com.android.server.usb.hal.port.UsbPortHal.HAL_MODE_UFP; import static com.android.internal.usb.DumpUtils.writePort; import static com.android.internal.usb.DumpUtils.writePortStatus; @@ -38,6 +46,7 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.res.Resources; +import android.hardware.usb.IUsbOperationInternal; import android.hardware.usb.ParcelableUsbPort; import android.hardware.usb.UsbManager; import android.hardware.usb.UsbPort; @@ -74,9 +83,13 @@ import com.android.internal.util.FrameworkStatsLog; import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.dump.DualDumpOutputStream; import com.android.server.FgThread; +import com.android.server.usb.hal.port.RawPortInfo; +import com.android.server.usb.hal.port.UsbPortHal; +import com.android.server.usb.hal.port.UsbPortHalInstance; import java.util.ArrayList; import java.util.NoSuchElementException; +import java.util.Objects; /** * Allows trusted components to control the properties of physical USB ports @@ -109,16 +122,9 @@ public class UsbPortManager { // The system context. private final Context mContext; - // Proxy object for the usb hal daemon. - @GuardedBy("mLock") - private IUsb mProxy = null; - // Callback when the UsbPort status is changed by the kernel. // Mostly due a command sent by the remote Usb device. - private HALCallback mHALCallback = new HALCallback(null, this); - - // Cookie sent for usb hal death notification. - private static final int USB_HAL_DEATH_COOKIE = 1000; + //private HALCallback mHALCallback = new HALCallback(null, this); // Used as the key while sending the bundle to Main thread. private static final String PORT_INFO = "port_info"; @@ -156,36 +162,23 @@ public class UsbPortManager { */ private int mIsPortContaminatedNotificationId; - private boolean mEnableUsbDataSignaling; - protected int mCurrentUsbHalVersion; + private UsbPortHal mUsbPortHal; + + private long mTransactionId; public UsbPortManager(Context context) { mContext = context; - try { - ServiceNotification serviceNotification = new ServiceNotification(); - - boolean ret = IServiceManager.getService() - .registerForNotifications("android.hardware.usb@1.0::IUsb", - "", serviceNotification); - if (!ret) { - logAndPrint(Log.ERROR, null, - "Failed to register service start notification"); - } - } catch (RemoteException e) { - logAndPrintException(null, - "Failed to register service start notification", e); - return; - } - connectToProxy(null); + mUsbPortHal = UsbPortHalInstance.getInstance(this, null); + logAndPrint(Log.DEBUG, null, "getInstance done"); } public void systemReady() { - mSystemReady = true; - if (mProxy != null) { + mSystemReady = true; + if (mUsbPortHal != null) { + mUsbPortHal.systemReady(); try { - mProxy.queryPortStatus(); - mEnableUsbDataSignaling = true; - } catch (RemoteException e) { + mUsbPortHal.queryPortStatus(++mTransactionId); + } catch (Exception e) { logAndPrintException(null, "ServiceStart: Failed to query port status", e); } @@ -340,13 +333,9 @@ public class UsbPortManager { } try { - // Oneway call into the hal. Use the castFrom method from HIDL. - android.hardware.usb.V1_2.IUsb proxy = android.hardware.usb.V1_2.IUsb.castFrom(mProxy); - proxy.enableContaminantPresenceDetection(portId, enable); - } catch (RemoteException e) { + mUsbPortHal.enableContaminantPresenceDetection(portId, enable, ++mTransactionId); + } catch (Exception e) { logAndPrintException(pw, "Failed to set contaminant detection", e); - } catch (ClassCastException e) { - logAndPrintException(pw, "Method only applicable to V1.2 or above implementation", e); } } @@ -355,46 +344,79 @@ public class UsbPortManager { * * @param enable enable or disable USB data signaling */ - public boolean enableUsbDataSignal(boolean enable) { + public boolean enableUsbData(@NonNull String portId, boolean enable, int transactionId, + @NonNull IUsbOperationInternal callback, IndentingPrintWriter pw) { + Objects.requireNonNull(callback); + Objects.requireNonNull(portId); + final PortInfo portInfo = mPorts.get(portId); + if (portInfo == null) { + logAndPrint(Log.ERROR, pw, "enableUsbData: No such port: " + portId + + " opId:" + transactionId); + try { + callback.onOperationComplete(USB_OPERATION_ERROR_PORT_MISMATCH); + } catch (RemoteException e) { + logAndPrintException(pw, + "enableUsbData: Failed to call OperationComplete. opId:" + + transactionId, e); + } + return false; + } + try { - mEnableUsbDataSignaling = enable; - // Call into the hal. Use the castFrom method from HIDL. - android.hardware.usb.V1_3.IUsb proxy = android.hardware.usb.V1_3.IUsb.castFrom(mProxy); - return proxy.enableUsbDataSignal(enable); + try { + return mUsbPortHal.enableUsbData(portId, enable, transactionId, callback); + } catch (Exception e) { + logAndPrintException(pw, + "enableUsbData: Failed to invoke enableUsbData. opId:" + + transactionId , e); + callback.onOperationComplete(USB_OPERATION_ERROR_INTERNAL); + } } catch (RemoteException e) { - logAndPrintException(null, "Failed to set USB data signaling", e); - return false; - } catch (ClassCastException e) { - logAndPrintException(null, "Method only applicable to V1.3 or above implementation", e); - return false; + logAndPrintException(pw, + "enableUsbData: Failed to call onOperationComplete. opId:" + + transactionId, e); } + + return false; } /** * Get USB HAL version * * @param none + * @return {@link UsbManager#USB_HAL_RETRY} returned when hal version + * is yet to be determined. */ public int getUsbHalVersion() { - return mCurrentUsbHalVersion; + if (mUsbPortHal != null) { + try { + return mUsbPortHal.getUsbHalVersion(); + } catch (RemoteException e) { + return UsbManager.USB_HAL_RETRY; + } + } + return UsbManager.USB_HAL_RETRY; } - /** - * update USB HAL version - * - * @param none - */ - private void updateUsbHalVersion() { - if (android.hardware.usb.V1_3.IUsb.castFrom(mProxy) != null) { - mCurrentUsbHalVersion = UsbManager.USB_HAL_V1_3; - } else if (android.hardware.usb.V1_2.IUsb.castFrom(mProxy) != null) { - mCurrentUsbHalVersion = UsbManager.USB_HAL_V1_2; - } else if (android.hardware.usb.V1_1.IUsb.castFrom(mProxy) != null) { - mCurrentUsbHalVersion = UsbManager.USB_HAL_V1_1; - } else { - mCurrentUsbHalVersion = UsbManager.USB_HAL_V1_0; - } - logAndPrint(Log.INFO, null, "USB HAL version: " + mCurrentUsbHalVersion); + private int toHalUsbDataRole(int usbDataRole) { + if (usbDataRole == DATA_ROLE_DEVICE) + return HAL_DATA_ROLE_DEVICE; + else + return HAL_DATA_ROLE_HOST; + } + + private int toHalUsbPowerRole(int usbPowerRole) { + if (usbPowerRole == POWER_ROLE_SINK) + return HAL_POWER_ROLE_SINK; + else + return HAL_POWER_ROLE_SOURCE; + } + + private int toHalUsbMode(int usbMode) { + if (usbMode == MODE_UFP) + return HAL_MODE_UFP; + else + return HAL_MODE_DFP; } public void setPortRoles(String portId, int newPowerRole, int newDataRole, @@ -473,7 +495,7 @@ public class UsbPortManager { sim.currentPowerRole = newPowerRole; sim.currentDataRole = newDataRole; updatePortsLocked(pw, null); - } else if (mProxy != null) { + } else if (mUsbPortHal != null) { if (currentMode != newMode) { // Changing the mode will have the side-effect of also changing // the power and data roles but it might take some time to apply @@ -485,44 +507,37 @@ public class UsbPortManager { logAndPrint(Log.ERROR, pw, "Trying to set the USB port mode: " + "portId=" + portId + ", newMode=" + UsbPort.modeToString(newMode)); - PortRole newRole = new PortRole(); - newRole.type = PortRoleType.MODE; - newRole.role = newMode; try { - mProxy.switchRole(portId, newRole); - } catch (RemoteException e) { + mUsbPortHal.switchMode(portId, toHalUsbMode(newMode), ++mTransactionId); + } catch (Exception e) { logAndPrintException(pw, "Failed to set the USB port mode: " + "portId=" + portId - + ", newMode=" + UsbPort.modeToString(newRole.role), e); + + ", newMode=" + UsbPort.modeToString(newMode), e); } } else { // Change power and data role independently as needed. if (currentPowerRole != newPowerRole) { - PortRole newRole = new PortRole(); - newRole.type = PortRoleType.POWER_ROLE; - newRole.role = newPowerRole; try { - mProxy.switchRole(portId, newRole); - } catch (RemoteException e) { + mUsbPortHal.switchPowerRole(portId, toHalUsbPowerRole(newPowerRole), + ++mTransactionId); + } catch (Exception e) { logAndPrintException(pw, "Failed to set the USB port power role: " + "portId=" + portId + ", newPowerRole=" + UsbPort.powerRoleToString - (newRole.role), + (newPowerRole), e); return; } } if (currentDataRole != newDataRole) { - PortRole newRole = new PortRole(); - newRole.type = PortRoleType.DATA_ROLE; - newRole.role = newDataRole; try { - mProxy.switchRole(portId, newRole); - } catch (RemoteException e) { + mUsbPortHal.switchDataRole(portId, toHalUsbDataRole(newDataRole), + ++mTransactionId); + } catch (Exception e) { logAndPrintException(pw, "Failed to set the USB port data role: " + "portId=" + portId - + ", newDataRole=" + UsbPort.dataRoleToString(newRole - .role), + + ", newDataRole=" + UsbPort.dataRoleToString + (newDataRole), e); } } @@ -531,6 +546,15 @@ public class UsbPortManager { } } + public void updatePorts(ArrayList<RawPortInfo> newPortInfo) { + Message message = mHandler.obtainMessage(); + Bundle bundle = new Bundle(); + bundle.putParcelableArrayList(PORT_INFO, newPortInfo); + message.what = MSG_UPDATE_PORTS; + message.setData(bundle); + mHandler.sendMessage(message); + } + public void addSimulatedPort(String portId, int supportedModes, IndentingPrintWriter pw) { synchronized (mLock) { if (mSimulatedPorts.containsKey(portId)) { @@ -662,191 +686,12 @@ public class UsbPortManager { portInfo.dump(dump, "usb_ports", UsbPortManagerProto.USB_PORTS); } - dump.write("enable_usb_data_signaling", UsbPortManagerProto.ENABLE_USB_DATA_SIGNALING, - mEnableUsbDataSignaling); + dump.write("usb_hal_version", UsbPortManagerProto.HAL_VERSION, getUsbHalVersion()); } dump.end(token); } - private static class HALCallback extends IUsbCallback.Stub { - public IndentingPrintWriter pw; - public UsbPortManager portManager; - - HALCallback(IndentingPrintWriter pw, UsbPortManager portManager) { - this.pw = pw; - this.portManager = portManager; - } - - public void notifyPortStatusChange( - ArrayList<android.hardware.usb.V1_0.PortStatus> currentPortStatus, int retval) { - if (!portManager.mSystemReady) { - return; - } - - if (retval != Status.SUCCESS) { - logAndPrint(Log.ERROR, pw, "port status enquiry failed"); - return; - } - - ArrayList<RawPortInfo> newPortInfo = new ArrayList<>(); - - for (android.hardware.usb.V1_0.PortStatus current : currentPortStatus) { - RawPortInfo temp = new RawPortInfo(current.portName, - current.supportedModes, CONTAMINANT_PROTECTION_NONE, - current.currentMode, - current.canChangeMode, current.currentPowerRole, - current.canChangePowerRole, - current.currentDataRole, current.canChangeDataRole, - false, CONTAMINANT_PROTECTION_NONE, - false, CONTAMINANT_DETECTION_NOT_SUPPORTED); - newPortInfo.add(temp); - logAndPrint(Log.INFO, pw, "ClientCallback V1_0: " + current.portName); - } - - Message message = portManager.mHandler.obtainMessage(); - Bundle bundle = new Bundle(); - bundle.putParcelableArrayList(PORT_INFO, newPortInfo); - message.what = MSG_UPDATE_PORTS; - message.setData(bundle); - portManager.mHandler.sendMessage(message); - } - - - public void notifyPortStatusChange_1_1(ArrayList<PortStatus_1_1> currentPortStatus, - int retval) { - if (!portManager.mSystemReady) { - return; - } - - if (retval != Status.SUCCESS) { - logAndPrint(Log.ERROR, pw, "port status enquiry failed"); - return; - } - - ArrayList<RawPortInfo> newPortInfo = new ArrayList<>(); - - int numStatus = currentPortStatus.size(); - for (int i = 0; i < numStatus; i++) { - PortStatus_1_1 current = currentPortStatus.get(i); - RawPortInfo temp = new RawPortInfo(current.status.portName, - current.supportedModes, CONTAMINANT_PROTECTION_NONE, - current.currentMode, - current.status.canChangeMode, current.status.currentPowerRole, - current.status.canChangePowerRole, - current.status.currentDataRole, current.status.canChangeDataRole, - false, CONTAMINANT_PROTECTION_NONE, - false, CONTAMINANT_DETECTION_NOT_SUPPORTED); - newPortInfo.add(temp); - logAndPrint(Log.INFO, pw, "ClientCallback V1_1: " + current.status.portName); - } - - Message message = portManager.mHandler.obtainMessage(); - Bundle bundle = new Bundle(); - bundle.putParcelableArrayList(PORT_INFO, newPortInfo); - message.what = MSG_UPDATE_PORTS; - message.setData(bundle); - portManager.mHandler.sendMessage(message); - } - - public void notifyPortStatusChange_1_2( - ArrayList<PortStatus> currentPortStatus, int retval) { - if (!portManager.mSystemReady) { - return; - } - - if (retval != Status.SUCCESS) { - logAndPrint(Log.ERROR, pw, "port status enquiry failed"); - return; - } - - ArrayList<RawPortInfo> newPortInfo = new ArrayList<>(); - - int numStatus = currentPortStatus.size(); - for (int i = 0; i < numStatus; i++) { - PortStatus current = currentPortStatus.get(i); - RawPortInfo temp = new RawPortInfo(current.status_1_1.status.portName, - current.status_1_1.supportedModes, - current.supportedContaminantProtectionModes, - current.status_1_1.currentMode, - current.status_1_1.status.canChangeMode, - current.status_1_1.status.currentPowerRole, - current.status_1_1.status.canChangePowerRole, - current.status_1_1.status.currentDataRole, - current.status_1_1.status.canChangeDataRole, - current.supportsEnableContaminantPresenceProtection, - current.contaminantProtectionStatus, - current.supportsEnableContaminantPresenceDetection, - current.contaminantDetectionStatus); - newPortInfo.add(temp); - logAndPrint(Log.INFO, pw, "ClientCallback V1_2: " - + current.status_1_1.status.portName); - } - - Message message = portManager.mHandler.obtainMessage(); - Bundle bundle = new Bundle(); - bundle.putParcelableArrayList(PORT_INFO, newPortInfo); - message.what = MSG_UPDATE_PORTS; - message.setData(bundle); - portManager.mHandler.sendMessage(message); - } - - public void notifyRoleSwitchStatus(String portName, PortRole role, int retval) { - if (retval == Status.SUCCESS) { - logAndPrint(Log.INFO, pw, portName + " role switch successful"); - } else { - logAndPrint(Log.ERROR, pw, portName + " role switch failed"); - } - } - } - - final class DeathRecipient implements HwBinder.DeathRecipient { - public IndentingPrintWriter pw; - - DeathRecipient(IndentingPrintWriter pw) { - this.pw = pw; - } - - @Override - public void serviceDied(long cookie) { - if (cookie == USB_HAL_DEATH_COOKIE) { - logAndPrint(Log.ERROR, pw, "Usb hal service died cookie: " + cookie); - synchronized (mLock) { - mProxy = null; - } - } - } - } - - final class ServiceNotification extends IServiceNotification.Stub { - @Override - public void onRegistration(String fqName, String name, boolean preexisting) { - logAndPrint(Log.INFO, null, "Usb hal service started " + fqName + " " + name); - connectToProxy(null); - } - } - - private void connectToProxy(IndentingPrintWriter pw) { - synchronized (mLock) { - if (mProxy != null) { - return; - } - - try { - mProxy = IUsb.getService(); - mProxy.linkToDeath(new DeathRecipient(pw), USB_HAL_DEATH_COOKIE); - mProxy.setCallback(mHALCallback); - mProxy.queryPortStatus(); - updateUsbHalVersion(); - } catch (NoSuchElementException e) { - logAndPrintException(pw, "connectToProxy: usb hal service not found." - + " Did the service fail to start?", e); - } catch (RemoteException e) { - logAndPrintException(pw, "connectToProxy: usb hal service not responding", e); - } - } - } - /** * Simulated ports directly add the new roles to mSimulatedPorts before calling. * USB hal callback populates and sends the newPortInfo. @@ -869,7 +714,8 @@ public class UsbPortManager { portInfo.supportsEnableContaminantPresenceProtection, portInfo.contaminantProtectionStatus, portInfo.supportsEnableContaminantPresenceDetection, - portInfo.contaminantDetectionStatus, pw); + portInfo.contaminantDetectionStatus, + portInfo.usbDataEnabled, pw); } } else { for (RawPortInfo currentPortInfo : newPortInfo) { @@ -881,7 +727,8 @@ public class UsbPortManager { currentPortInfo.supportsEnableContaminantPresenceProtection, currentPortInfo.contaminantProtectionStatus, currentPortInfo.supportsEnableContaminantPresenceDetection, - currentPortInfo.contaminantDetectionStatus, pw); + currentPortInfo.contaminantDetectionStatus, + currentPortInfo.usbDataEnabled, pw); } } @@ -917,6 +764,7 @@ public class UsbPortManager { int contaminantProtectionStatus, boolean supportsEnableContaminantPresenceDetection, int contaminantDetectionStatus, + boolean usbDataEnabled, IndentingPrintWriter pw) { // Only allow mode switch capability for dual role ports. // Validate that the current mode matches the supported modes we expect. @@ -975,7 +823,7 @@ public class UsbPortManager { currentPowerRole, canChangePowerRole, currentDataRole, canChangeDataRole, supportedRoleCombinations, contaminantProtectionStatus, - contaminantDetectionStatus); + contaminantDetectionStatus, usbDataEnabled); mPorts.put(portId, portInfo); } else { // Validate that ports aren't changing definition out from under us. @@ -1012,7 +860,7 @@ public class UsbPortManager { currentPowerRole, canChangePowerRole, currentDataRole, canChangeDataRole, supportedRoleCombinations, contaminantProtectionStatus, - contaminantDetectionStatus)) { + contaminantDetectionStatus, usbDataEnabled)) { portInfo.mDisposition = PortInfo.DISPOSITION_CHANGED; } else { portInfo.mDisposition = PortInfo.DISPOSITION_READY; @@ -1141,14 +989,14 @@ public class UsbPortManager { } } - private static void logAndPrint(int priority, IndentingPrintWriter pw, String msg) { + public static void logAndPrint(int priority, IndentingPrintWriter pw, String msg) { Slog.println(priority, TAG, msg); if (pw != null) { pw.println(msg); } } - private static void logAndPrintException(IndentingPrintWriter pw, String msg, Exception e) { + public static void logAndPrintException(IndentingPrintWriter pw, String msg, Exception e) { Slog.e(TAG, msg, e); if (pw != null) { pw.println(msg + e); @@ -1179,7 +1027,7 @@ public class UsbPortManager { /** * Describes a USB port. */ - private static final class PortInfo { + public static final class PortInfo { public static final int DISPOSITION_ADDED = 0; public static final int DISPOSITION_CHANGED = 1; public static final int DISPOSITION_READY = 2; @@ -1224,7 +1072,7 @@ public class UsbPortManager { != supportedRoleCombinations) { mUsbPortStatus = new UsbPortStatus(currentMode, currentPowerRole, currentDataRole, supportedRoleCombinations, UsbPortStatus.CONTAMINANT_PROTECTION_NONE, - UsbPortStatus.CONTAMINANT_DETECTION_NOT_SUPPORTED); + UsbPortStatus.CONTAMINANT_DETECTION_NOT_SUPPORTED, true); dispositionChanged = true; } @@ -1243,7 +1091,7 @@ public class UsbPortManager { int currentPowerRole, boolean canChangePowerRole, int currentDataRole, boolean canChangeDataRole, int supportedRoleCombinations, int contaminantProtectionStatus, - int contaminantDetectionStatus) { + int contaminantDetectionStatus, boolean usbDataEnabled) { boolean dispositionChanged = false; mCanChangeMode = canChangeMode; @@ -1258,10 +1106,12 @@ public class UsbPortManager { || mUsbPortStatus.getContaminantProtectionStatus() != contaminantProtectionStatus || mUsbPortStatus.getContaminantDetectionStatus() - != contaminantDetectionStatus) { + != contaminantDetectionStatus + || mUsbPortStatus.getUsbDataStatus() + != usbDataEnabled){ mUsbPortStatus = new UsbPortStatus(currentMode, currentPowerRole, currentDataRole, supportedRoleCombinations, contaminantProtectionStatus, - contaminantDetectionStatus); + contaminantDetectionStatus, usbDataEnabled); dispositionChanged = true; } @@ -1290,7 +1140,6 @@ public class UsbPortManager { UsbPortInfoProto.CONNECTED_AT_MILLIS, mConnectedAtMillis); dump.write("last_connect_duration_millis", UsbPortInfoProto.LAST_CONNECT_DURATION_MILLIS, mLastConnectDurationMillis); - dump.end(token); } @@ -1304,115 +1153,4 @@ public class UsbPortManager { + ", lastConnectDurationMillis=" + mLastConnectDurationMillis; } } - - /** - * Used for storing the raw data from the kernel - * Values of the member variables mocked directly incase of emulation. - */ - private static final class RawPortInfo implements Parcelable { - public final String portId; - public final int supportedModes; - public final int supportedContaminantProtectionModes; - public int currentMode; - public boolean canChangeMode; - public int currentPowerRole; - public boolean canChangePowerRole; - public int currentDataRole; - public boolean canChangeDataRole; - public boolean supportsEnableContaminantPresenceProtection; - public int contaminantProtectionStatus; - public boolean supportsEnableContaminantPresenceDetection; - public int contaminantDetectionStatus; - - RawPortInfo(String portId, int supportedModes) { - this.portId = portId; - this.supportedModes = supportedModes; - this.supportedContaminantProtectionModes = UsbPortStatus.CONTAMINANT_PROTECTION_NONE; - this.supportsEnableContaminantPresenceProtection = false; - this.contaminantProtectionStatus = UsbPortStatus.CONTAMINANT_PROTECTION_NONE; - this.supportsEnableContaminantPresenceDetection = false; - this.contaminantDetectionStatus = UsbPortStatus.CONTAMINANT_DETECTION_NOT_SUPPORTED; - } - - RawPortInfo(String portId, int supportedModes, int supportedContaminantProtectionModes, - int currentMode, boolean canChangeMode, - int currentPowerRole, boolean canChangePowerRole, - int currentDataRole, boolean canChangeDataRole, - boolean supportsEnableContaminantPresenceProtection, - int contaminantProtectionStatus, - boolean supportsEnableContaminantPresenceDetection, - int contaminantDetectionStatus) { - this.portId = portId; - this.supportedModes = supportedModes; - this.supportedContaminantProtectionModes = supportedContaminantProtectionModes; - this.currentMode = currentMode; - this.canChangeMode = canChangeMode; - this.currentPowerRole = currentPowerRole; - this.canChangePowerRole = canChangePowerRole; - this.currentDataRole = currentDataRole; - this.canChangeDataRole = canChangeDataRole; - this.supportsEnableContaminantPresenceProtection = - supportsEnableContaminantPresenceProtection; - this.contaminantProtectionStatus = contaminantProtectionStatus; - this.supportsEnableContaminantPresenceDetection = - supportsEnableContaminantPresenceDetection; - this.contaminantDetectionStatus = contaminantDetectionStatus; - } - - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeString(portId); - dest.writeInt(supportedModes); - dest.writeInt(supportedContaminantProtectionModes); - dest.writeInt(currentMode); - dest.writeByte((byte) (canChangeMode ? 1 : 0)); - dest.writeInt(currentPowerRole); - dest.writeByte((byte) (canChangePowerRole ? 1 : 0)); - dest.writeInt(currentDataRole); - dest.writeByte((byte) (canChangeDataRole ? 1 : 0)); - dest.writeBoolean(supportsEnableContaminantPresenceProtection); - dest.writeInt(contaminantProtectionStatus); - dest.writeBoolean(supportsEnableContaminantPresenceDetection); - dest.writeInt(contaminantDetectionStatus); - } - - public static final Parcelable.Creator<RawPortInfo> CREATOR = - new Parcelable.Creator<RawPortInfo>() { - @Override - public RawPortInfo createFromParcel(Parcel in) { - String id = in.readString(); - int supportedModes = in.readInt(); - int supportedContaminantProtectionModes = in.readInt(); - int currentMode = in.readInt(); - boolean canChangeMode = in.readByte() != 0; - int currentPowerRole = in.readInt(); - boolean canChangePowerRole = in.readByte() != 0; - int currentDataRole = in.readInt(); - boolean canChangeDataRole = in.readByte() != 0; - boolean supportsEnableContaminantPresenceProtection = in.readBoolean(); - int contaminantProtectionStatus = in.readInt(); - boolean supportsEnableContaminantPresenceDetection = in.readBoolean(); - int contaminantDetectionStatus = in.readInt(); - return new RawPortInfo(id, supportedModes, - supportedContaminantProtectionModes, currentMode, canChangeMode, - currentPowerRole, canChangePowerRole, - currentDataRole, canChangeDataRole, - supportsEnableContaminantPresenceProtection, - contaminantProtectionStatus, - supportsEnableContaminantPresenceDetection, - contaminantDetectionStatus); - } - - @Override - public RawPortInfo[] newArray(int size) { - return new RawPortInfo[size]; - } - }; - } } diff --git a/services/usb/java/com/android/server/usb/UsbService.java b/services/usb/java/com/android/server/usb/UsbService.java index 3d3538d7ae49..28227fcf0468 100644 --- a/services/usb/java/com/android/server/usb/UsbService.java +++ b/services/usb/java/com/android/server/usb/UsbService.java @@ -16,6 +16,7 @@ package com.android.server.usb; +import static android.hardware.usb.UsbOperationInternal.USB_OPERATION_ERROR_INTERNAL; import static android.hardware.usb.UsbPortStatus.DATA_ROLE_DEVICE; import static android.hardware.usb.UsbPortStatus.DATA_ROLE_HOST; import static android.hardware.usb.UsbPortStatus.MODE_DFP; @@ -35,6 +36,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; import android.hardware.usb.IUsbManager; +import android.hardware.usb.IUsbOperationInternal; import android.hardware.usb.ParcelableUsbPort; import android.hardware.usb.UsbAccessory; import android.hardware.usb.UsbDevice; @@ -44,6 +46,7 @@ import android.hardware.usb.UsbPortStatus; import android.os.Binder; import android.os.Bundle; import android.os.ParcelFileDescriptor; +import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; import android.service.usb.UsbServiceDumpProto; @@ -762,19 +765,30 @@ public class UsbService extends IUsbManager.Stub { } @Override - public boolean enableUsbDataSignal(boolean enable) { + public boolean enableUsbData(String portId, boolean enable, int operationId, + IUsbOperationInternal callback) { + Objects.requireNonNull(portId, "enableUsbData: portId must not be null. opId:" + + operationId); + Objects.requireNonNull(callback, "enableUsbData: 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) { - return mPortManager.enableUsbDataSignal(enable); + wait = mPortManager.enableUsbData(portId, enable, operationId, callback, null); } else { - return false; + wait = false; + try { + callback.onOperationComplete(USB_OPERATION_ERROR_INTERNAL); + } catch (RemoteException e) { + Slog.e(TAG, "enableUsbData: Failed to call onOperationComplete", e); + } } } finally { Binder.restoreCallingIdentity(ident); } + return wait; } @Override 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 new file mode 100644 index 000000000000..9c6cbbd96460 --- /dev/null +++ b/services/usb/java/com/android/server/usb/hal/port/RawPortInfo.java @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2021 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 com.android.server.usb.hal.port; + +import android.hardware.usb.UsbPortStatus; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * Used for storing the raw data from the HAL. + * Values of the member variables mocked directly in case of emulation. + */ +public final class RawPortInfo implements Parcelable { + public final String portId; + public final int supportedModes; + public final int supportedContaminantProtectionModes; + public int currentMode; + public boolean canChangeMode; + public int currentPowerRole; + public boolean canChangePowerRole; + public int currentDataRole; + public boolean canChangeDataRole; + public boolean supportsEnableContaminantPresenceProtection; + public int contaminantProtectionStatus; + public boolean supportsEnableContaminantPresenceDetection; + public int contaminantDetectionStatus; + public boolean usbDataEnabled; + + public RawPortInfo(String portId, int supportedModes) { + this.portId = portId; + this.supportedModes = supportedModes; + this.supportedContaminantProtectionModes = UsbPortStatus.CONTAMINANT_PROTECTION_NONE; + this.supportsEnableContaminantPresenceProtection = false; + this.contaminantProtectionStatus = UsbPortStatus.CONTAMINANT_PROTECTION_NONE; + this.supportsEnableContaminantPresenceDetection = false; + this.contaminantDetectionStatus = UsbPortStatus.CONTAMINANT_DETECTION_NOT_SUPPORTED; + this.usbDataEnabled = true; + } + + public RawPortInfo(String portId, int supportedModes, int supportedContaminantProtectionModes, + int currentMode, boolean canChangeMode, + int currentPowerRole, boolean canChangePowerRole, + int currentDataRole, boolean canChangeDataRole, + boolean supportsEnableContaminantPresenceProtection, + int contaminantProtectionStatus, + boolean supportsEnableContaminantPresenceDetection, + int contaminantDetectionStatus, + boolean usbDataEnabled) { + this.portId = portId; + this.supportedModes = supportedModes; + this.supportedContaminantProtectionModes = supportedContaminantProtectionModes; + this.currentMode = currentMode; + this.canChangeMode = canChangeMode; + this.currentPowerRole = currentPowerRole; + this.canChangePowerRole = canChangePowerRole; + this.currentDataRole = currentDataRole; + this.canChangeDataRole = canChangeDataRole; + this.supportsEnableContaminantPresenceProtection = + supportsEnableContaminantPresenceProtection; + this.contaminantProtectionStatus = contaminantProtectionStatus; + this.supportsEnableContaminantPresenceDetection = + supportsEnableContaminantPresenceDetection; + this.contaminantDetectionStatus = contaminantDetectionStatus; + this.usbDataEnabled = usbDataEnabled; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(portId); + dest.writeInt(supportedModes); + dest.writeInt(supportedContaminantProtectionModes); + dest.writeInt(currentMode); + dest.writeByte((byte) (canChangeMode ? 1 : 0)); + dest.writeInt(currentPowerRole); + dest.writeByte((byte) (canChangePowerRole ? 1 : 0)); + dest.writeInt(currentDataRole); + dest.writeByte((byte) (canChangeDataRole ? 1 : 0)); + dest.writeBoolean(supportsEnableContaminantPresenceProtection); + dest.writeInt(contaminantProtectionStatus); + dest.writeBoolean(supportsEnableContaminantPresenceDetection); + dest.writeInt(contaminantDetectionStatus); + dest.writeBoolean(usbDataEnabled); + } + + public static final Parcelable.Creator<RawPortInfo> CREATOR = + new Parcelable.Creator<RawPortInfo>() { + @Override + public RawPortInfo createFromParcel(Parcel in) { + String id = in.readString(); + int supportedModes = in.readInt(); + int supportedContaminantProtectionModes = in.readInt(); + int currentMode = in.readInt(); + boolean canChangeMode = in.readByte() != 0; + int currentPowerRole = in.readInt(); + boolean canChangePowerRole = in.readByte() != 0; + int currentDataRole = in.readInt(); + boolean canChangeDataRole = in.readByte() != 0; + boolean supportsEnableContaminantPresenceProtection = in.readBoolean(); + int contaminantProtectionStatus = in.readInt(); + boolean supportsEnableContaminantPresenceDetection = in.readBoolean(); + int contaminantDetectionStatus = in.readInt(); + boolean usbDataEnabled = in.readBoolean(); + return new RawPortInfo(id, supportedModes, + supportedContaminantProtectionModes, currentMode, canChangeMode, + currentPowerRole, canChangePowerRole, + currentDataRole, canChangeDataRole, + supportsEnableContaminantPresenceProtection, + contaminantProtectionStatus, + supportsEnableContaminantPresenceDetection, + contaminantDetectionStatus, usbDataEnabled); + } + + @Override + public RawPortInfo[] newArray(int size) { + return new RawPortInfo[size]; + } + }; +} 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 new file mode 100644 index 000000000000..6f91a2a56280 --- /dev/null +++ b/services/usb/java/com/android/server/usb/hal/port/UsbPortAidl.java @@ -0,0 +1,466 @@ +/* + * Copyright (C) 2021 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 com.android.server.usb.hal.port; + +import static android.hardware.usb.UsbManager.USB_HAL_V2_0; +import static android.hardware.usb.UsbOperationInternal.USB_OPERATION_ERROR_INTERNAL; +import static android.hardware.usb.UsbOperationInternal.USB_OPERATION_SUCCESS; + +import static com.android.server.usb.UsbPortManager.logAndPrint; +import static com.android.server.usb.UsbPortManager.logAndPrintException; + +import android.annotation.Nullable; +import android.hardware.usb.ContaminantProtectionStatus; +import android.hardware.usb.IUsb; +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.PortMode; +import android.hardware.usb.Status; +import android.hardware.usb.IUsbCallback; +import android.hardware.usb.PortRole; +import android.hardware.usb.PortStatus; +import android.os.ServiceManager; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Log; +import android.util.LongSparseArray; +import android.util.Slog; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.util.IndentingPrintWriter; +import com.android.server.usb.UsbPortManager; +import com.android.server.usb.hal.port.RawPortInfo; + +import java.util.ArrayList; +import java.util.concurrent.ThreadLocalRandom; +import java.util.NoSuchElementException; +import java.util.Objects; + +/** + * Implements the methods to interact with AIDL USB HAL. + */ +public final class UsbPortAidl implements UsbPortHal { + private static final String TAG = UsbPortAidl.class.getSimpleName(); + private static final String USB_AIDL_SERVICE = + "android.hardware.usb.IUsb/default"; + private static final LongSparseArray<IUsbOperationInternal> + sCallbacks = new LongSparseArray<>(); + // Proxy object for the usb hal daemon. + @GuardedBy("mLock") + private IUsb mProxy; + private UsbPortManager mPortManager; + public IndentingPrintWriter mPw; + // Mutex for all mutable shared state. + private final Object mLock = new Object(); + // Callback when the UsbPort status is changed by the kernel. + private HALCallback mHALCallback; + private IBinder mBinder; + private boolean mSystemReady; + private long mTransactionId; + + public @UsbHalVersion int getUsbHalVersion() throws RemoteException { + synchronized (mLock) { + if (mProxy == null) { + throw new RemoteException("IUsb not initialized yet"); + } + } + logAndPrint(Log.INFO, null, "USB HAL AIDL version: USB_HAL_V2_0"); + return USB_HAL_V2_0; + } + + @Override + public void systemReady() { + mSystemReady = true; + } + + public void serviceDied() { + logAndPrint(Log.ERROR, mPw, "Usb AIDL hal service died"); + synchronized (mLock) { + mProxy = null; + } + connectToProxy(null); + } + + private void connectToProxy(IndentingPrintWriter pw) { + synchronized (mLock) { + if (mProxy != null) { + return; + } + + try { + mBinder = ServiceManager.waitForService(USB_AIDL_SERVICE); + mProxy = IUsb.Stub.asInterface(mBinder); + mBinder.linkToDeath(this::serviceDied, 0); + mProxy.setCallback(mHALCallback); + mProxy.queryPortStatus(++mTransactionId); + } catch (NoSuchElementException e) { + logAndPrintException(pw, "connectToProxy: usb hal service not found." + + " Did the service fail to start?", e); + } catch (RemoteException e) { + logAndPrintException(pw, "connectToProxy: usb hal service not responding", e); + } + } + } + + static boolean isServicePresent(IndentingPrintWriter pw) { + try { + return ServiceManager.isDeclared(USB_AIDL_SERVICE); + } catch (NoSuchElementException e) { + logAndPrintException(pw, "connectToProxy: usb Aidl hal service not found.", e); + } + + return false; + } + + public UsbPortAidl(UsbPortManager portManager, IndentingPrintWriter pw) { + mPortManager = Objects.requireNonNull(portManager); + mPw = pw; + mHALCallback = new HALCallback(null, mPortManager, this); + connectToProxy(mPw); + } + + @Override + public void enableContaminantPresenceDetection(String portName, boolean enable, + long operationID) { + synchronized (mLock) { + if (mProxy == null) { + logAndPrint(Log.ERROR, mPw, "Proxy is null. Retry ! opID: " + + operationID); + return; + } + + try { + // Oneway call into the hal. Use the castFrom method from HIDL. + mProxy.enableContaminantPresenceDetection(portName, enable, operationID); + } catch (RemoteException e) { + logAndPrintException(mPw, "Failed to set contaminant detection. opID:" + + operationID, e); + } + } + } + + @Override + public void queryPortStatus(long operationID) { + synchronized (mLock) { + if (mProxy == null) { + logAndPrint(Log.ERROR, mPw, "Proxy is null. Retry ! opID:" + + operationID); + return; + } + + try { + mProxy.queryPortStatus(operationID); + } catch (RemoteException e) { + logAndPrintException(null, "ServiceStart: Failed to query port status. opID:" + + operationID, e); + } + } + } + + @Override + public void switchMode(String portId, @HalUsbPortMode int newMode, long operationID) { + synchronized (mLock) { + if (mProxy == null) { + logAndPrint(Log.ERROR, mPw, "Proxy is null. Retry ! opID:" + + operationID); + return; + } + + PortRole newRole = new PortRole(); + newRole.setMode((byte)newMode); + try { + mProxy.switchRole(portId, newRole, operationID); + } catch (RemoteException e) { + logAndPrintException(mPw, "Failed to set the USB port mode: " + + "portId=" + portId + + ", newMode=" + UsbPort.modeToString(newMode) + + "opID:" + operationID, e); + } + } + } + + @Override + public void switchPowerRole(String portId, @HalUsbPowerRole int newPowerRole, + long operationID) { + synchronized (mLock) { + if (mProxy == null) { + logAndPrint(Log.ERROR, mPw, "Proxy is null. Retry ! opID:" + + operationID); + return; + } + + PortRole newRole = new PortRole(); + newRole.setPowerRole((byte)newPowerRole); + try { + mProxy.switchRole(portId, newRole, operationID); + } catch (RemoteException e) { + logAndPrintException(mPw, "Failed to set the USB power role: portId=" + portId + + ", newPowerRole=" + UsbPort.powerRoleToString(newPowerRole) + + "opID:" + operationID, e); + } + } + } + + @Override + public void switchDataRole(String portId, @HalUsbDataRole int newDataRole, long operationID) { + synchronized (mLock) { + if (mProxy == null) { + logAndPrint(Log.ERROR, mPw, "Proxy is null. Retry ! opID:" + + operationID); + return; + } + + PortRole newRole = new PortRole(); + newRole.setDataRole((byte)newDataRole); + try { + mProxy.switchRole(portId, newRole, operationID); + } catch (RemoteException e) { + logAndPrintException(mPw, "Failed to set the USB data role: portId=" + portId + + ", newDataRole=" + UsbPort.dataRoleToString(newDataRole) + + "opID:" + operationID, e); + } + } + } + + @Override + public boolean enableUsbData(String portName, boolean enable, long operationID, + IUsbOperationInternal callback) { + Objects.requireNonNull(portName); + Objects.requireNonNull(callback); + long key = operationID; + synchronized (mLock) { + try { + if (mProxy == null) { + logAndPrint(Log.ERROR, mPw, + "enableUsbData: Proxy is null. Retry !opID:" + + operationID); + callback.onOperationComplete(USB_OPERATION_ERROR_INTERNAL); + return false; + } + 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.enableUsbData(portName, enable, key); + } catch (RemoteException e) { + logAndPrintException(mPw, + "enableUsbData: Failed to invoke enableUsbData: portID=" + + portName + "opID:" + operationID, e); + callback.onOperationComplete(USB_OPERATION_ERROR_INTERNAL); + sCallbacks.remove(key); + return false; + } + } catch (RemoteException e) { + logAndPrintException(mPw, + "enableUsbData: Failed to call onOperationComplete portID=" + + portName + "opID:" + operationID, e); + sCallbacks.remove(key); + return false; + } + return true; + } + } + + private static class HALCallback extends IUsbCallback.Stub { + public IndentingPrintWriter mPw; + public UsbPortManager mPortManager; + public UsbPortAidl mUsbPortAidl; + + HALCallback(IndentingPrintWriter pw, UsbPortManager portManager, UsbPortAidl usbPortAidl) { + this.mPw = pw; + this.mPortManager = portManager; + this.mUsbPortAidl = usbPortAidl; + } + + /** + * Converts from AIDL defined mode constants to UsbPortStatus constants. + * AIDL does not gracefully support bitfield when combined with enums. + */ + private int toPortMode(byte aidlPortMode) { + switch (aidlPortMode) { + case PortMode.NONE: + return UsbPortStatus.MODE_NONE; + case PortMode.UFP: + return UsbPortStatus.MODE_UFP; + case PortMode.DFP: + return UsbPortStatus.MODE_DFP; + case PortMode.DRP: + return UsbPortStatus.MODE_DUAL; + case PortMode.AUDIO_ACCESSORY: + return UsbPortStatus.MODE_AUDIO_ACCESSORY; + case PortMode.DEBUG_ACCESSORY: + return UsbPortStatus.MODE_DEBUG_ACCESSORY; + default: + UsbPortManager.logAndPrint(Log.ERROR, mPw, "Unrecognized aidlPortMode:" + + aidlPortMode); + return UsbPortStatus.MODE_NONE; + } + } + + private int toSupportedModes(byte[] aidlPortModes) { + int supportedModes = UsbPortStatus.MODE_NONE; + + for (byte aidlPortMode : aidlPortModes) { + supportedModes |= toPortMode(aidlPortMode); + } + + return supportedModes; + } + + /** + * Converts from AIDL defined contaminant protection constants to UsbPortStatus constants. + * AIDL does not gracefully support bitfield when combined with enums. + * Common to both ContaminantProtectionMode and ContaminantProtectionStatus. + */ + private int toContaminantProtectionStatus(byte aidlContaminantProtection) { + switch (aidlContaminantProtection) { + case ContaminantProtectionStatus.NONE: + return UsbPortStatus.CONTAMINANT_PROTECTION_NONE; + case ContaminantProtectionStatus.FORCE_SINK: + return UsbPortStatus.CONTAMINANT_PROTECTION_SINK; + case ContaminantProtectionStatus.FORCE_SOURCE: + return UsbPortStatus.CONTAMINANT_PROTECTION_SOURCE; + case ContaminantProtectionStatus.FORCE_DISABLE: + return UsbPortStatus.CONTAMINANT_PROTECTION_FORCE_DISABLE; + case ContaminantProtectionStatus.DISABLED: + return UsbPortStatus.CONTAMINANT_PROTECTION_DISABLED; + default: + UsbPortManager.logAndPrint(Log.ERROR, mPw, + "Unrecognized aidlContaminantProtection:" + + aidlContaminantProtection); + return UsbPortStatus.CONTAMINANT_PROTECTION_NONE; + } + } + + private int toSupportedContaminantProtectionModes(byte[] aidlModes) { + int supportedContaminantProtectionModes = UsbPortStatus.CONTAMINANT_PROTECTION_NONE; + + for (byte aidlMode : aidlModes) { + supportedContaminantProtectionModes |= toContaminantProtectionStatus(aidlMode); + } + + return supportedContaminantProtectionModes; + } + + @Override + public void notifyPortStatusChange( + android.hardware.usb.PortStatus[] currentPortStatus, int retval) { + if (!mUsbPortAidl.mSystemReady) { + return; + } + + if (retval != Status.SUCCESS) { + UsbPortManager.logAndPrint(Log.ERROR, mPw, "port status enquiry failed"); + return; + } + + ArrayList<RawPortInfo> newPortInfo = new ArrayList<>(); + + int numStatus = currentPortStatus.length; + for (int i = 0; i < numStatus; i++) { + PortStatus current = currentPortStatus[i]; + RawPortInfo temp = new RawPortInfo(current.portName, + toSupportedModes(current.supportedModes), + toSupportedContaminantProtectionModes(current + .supportedContaminantProtectionModes), + toPortMode(current.currentMode), + current.canChangeMode, + current.currentPowerRole, + current.canChangePowerRole, + current.currentDataRole, + current.canChangeDataRole, + current.supportsEnableContaminantPresenceProtection, + toContaminantProtectionStatus(current.contaminantProtectionStatus), + current.supportsEnableContaminantPresenceDetection, + current.contaminantDetectionStatus, + current.usbDataEnabled); + newPortInfo.add(temp); + UsbPortManager.logAndPrint(Log.INFO, mPw, "ClientCallback AIDL V1: " + + current.portName); + } + mPortManager.updatePorts(newPortInfo); + } + + @Override + public void notifyRoleSwitchStatus(String portName, PortRole role, int retval, + long operationID) { + if (retval == Status.SUCCESS) { + UsbPortManager.logAndPrint(Log.INFO, mPw, portName + + " role switch successful. opID:" + + operationID); + } else { + UsbPortManager.logAndPrint(Log.ERROR, mPw, portName + " role switch failed. err:" + + retval + + "opID:" + operationID); + } + } + + @Override + public void notifyQueryPortStatus(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 + ": opID:" + + operationID + " failed. err:" + retval); + } + } + + @Override + public void notifyEnableUsbDataStatus(String portName, boolean enable, int retval, + long operationID) { + if (retval == Status.SUCCESS) { + UsbPortManager.logAndPrint(Log.INFO, mPw, "notifyEnableUsbDataStatus:" + + portName + ": opID:" + + operationID + " enable:" + enable); + } else { + UsbPortManager.logAndPrint(Log.ERROR, mPw, portName + + "notifyEnableUsbDataStatus: opID:" + + operationID + " failed. err:" + retval); + } + try { + sCallbacks.get(operationID).onOperationComplete(retval == Status.SUCCESS + ? USB_OPERATION_SUCCESS + : USB_OPERATION_ERROR_INTERNAL); + } catch (RemoteException e) { + logAndPrintException(mPw, + "notifyEnableUsbDataStatus: Failed to call onOperationComplete", + e); + } + } + + @Override + public void notifyContaminantEnabledStatus(String portName, boolean enable, int retval, + long operationID) { + if (retval == Status.SUCCESS) { + UsbPortManager.logAndPrint(Log.INFO, mPw, "notifyContaminantEnabledStatus:" + + portName + ": opID:" + + operationID + " enable:" + enable); + } else { + UsbPortManager.logAndPrint(Log.ERROR, mPw, portName + + "notifyContaminantEnabledStatus: opID:" + + operationID + " failed. err:" + retval); + } + } + } +} 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 new file mode 100644 index 000000000000..e7f9bc2fe7c3 --- /dev/null +++ b/services/usb/java/com/android/server/usb/hal/port/UsbPortHal.java @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2021 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 com.android.server.usb.hal.port; + +import android.annotation.IntDef; +import android.hardware.usb.IUsbOperationInternal; +import android.hardware.usb.UsbManager.UsbHalVersion; +import android.os.RemoteException; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.String; + +/** + * @hide + */ +public interface UsbPortHal { + /** + * Power role: This USB port can act as a source (provide power). + * @hide + */ + public static final int HAL_POWER_ROLE_SOURCE = 1; + + /** + * Power role: This USB port can act as a sink (receive power). + * @hide + */ + public static final int HAL_POWER_ROLE_SINK = 2; + + @IntDef(prefix = { "HAL_POWER_ROLE_" }, value = { + HAL_POWER_ROLE_SOURCE, + HAL_POWER_ROLE_SINK + }) + @Retention(RetentionPolicy.SOURCE) + @interface HalUsbPowerRole{} + + /** + * Data role: This USB port can act as a host (access data services). + * @hide + */ + public static final int HAL_DATA_ROLE_HOST = 1; + + /** + * Data role: This USB port can act as a device (offer data services). + * @hide + */ + public static final int HAL_DATA_ROLE_DEVICE = 2; + + @IntDef(prefix = { "HAL_DATA_ROLE_" }, value = { + HAL_DATA_ROLE_HOST, + HAL_DATA_ROLE_DEVICE + }) + @Retention(RetentionPolicy.SOURCE) + @interface HalUsbDataRole{} + + /** + * This USB port can act as a downstream facing port (host). + * + * @hide + */ + public static final int HAL_MODE_DFP = 1; + + /** + * This USB port can act as an upstream facing port (device). + * + * @hide + */ + public static final int HAL_MODE_UFP = 2; + @IntDef(prefix = { "HAL_MODE_" }, value = { + HAL_MODE_DFP, + HAL_MODE_UFP, + }) + @Retention(RetentionPolicy.SOURCE) + @interface HalUsbPortMode{} + + /** + * UsbPortManager would call this when the system is done booting. + */ + public void systemReady(); + + /** + * Invoked to enable/disable contaminant presence detection on the USB port. + * + * @param portName Port Identifier. + * @param enable Enable contaminant presence detection when true. + * Disable when false. + * @param transactionId Used for tracking the current request and is passed down to the HAL + * implementation as needed. + */ + public void enableContaminantPresenceDetection(String portName, boolean enable, + long transactionId); + + /** + * Invoked to query port status of all the ports. + * + * @param transactionId Used for tracking the current request and is passed down to the HAL + * implementation as needed. + */ + public void queryPortStatus(long transactionId); + + /** + * Invoked to switch USB port mode. + * + * @param portName Port Identifier. + * @param mode New mode that the port is switching into. + * @param transactionId Used for tracking the current request and is passed down to the HAL + * implementation as needed. + */ + public void switchMode(String portName, @HalUsbPortMode int mode, long transactionId); + + /** + * Invoked to switch USB port power role. + * + * @param portName Port Identifier. + * @param powerRole New power role that the port is switching into. + * @param transactionId Used for tracking the current request and is passed down to the HAL + * implementation as needed. + */ + public void switchPowerRole(String portName, @HalUsbPowerRole int powerRole, + long transactionId); + + /** + * Invoked to switch USB port data role. + * + * @param portName Port Identifier. + * @param dataRole New data role that the port is switching into. + * @param transactionId Used for tracking the current request and is passed down to the HAL + * implementation as needed. + */ + public void switchDataRole(String portName, @HalUsbDataRole int dataRole, long transactionId); + + /** + * Invoked to query the version of current hal implementation. + */ + public @UsbHalVersion int getUsbHalVersion() throws RemoteException; + + /** + * Invoked to enable/disable UsbData on the specified port. + * + * @param portName Port Identifier. + * @param enable Enable USB data when true. + * Disable 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. + * @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 + * the result. + * False when the operation is synchronous. Caller can proceed reading the result + * through {@link UsbOperationCallbackInternal#getStatus} + */ + public boolean enableUsbData(String portName, boolean enable, long transactionId, + IUsbOperationInternal callback); +} diff --git a/services/usb/java/com/android/server/usb/hal/port/UsbPortHalInstance.java b/services/usb/java/com/android/server/usb/hal/port/UsbPortHalInstance.java new file mode 100644 index 000000000000..41f9faef99df --- /dev/null +++ b/services/usb/java/com/android/server/usb/hal/port/UsbPortHalInstance.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2021 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 com.android.server.usb.hal.port; + +import static com.android.server.usb.UsbPortManager.logAndPrint; + +import com.android.internal.util.IndentingPrintWriter; +import com.android.server.usb.hal.port.UsbPortHidl; +import com.android.server.usb.hal.port.UsbPortAidl; +import com.android.server.usb.UsbPortManager; + +import android.util.Log; +/** + * Helper class that queries the underlying hal layer to populate UsbPortHal instance. + */ +public final class UsbPortHalInstance { + + public static UsbPortHal getInstance(UsbPortManager portManager, IndentingPrintWriter pw) { + + logAndPrint(Log.DEBUG, null, "Querying USB HAL version"); + if (UsbPortHidl.isServicePresent(null)) { + logAndPrint(Log.INFO, null, "USB HAL HIDL present"); + return new UsbPortHidl(portManager, pw); + } + if (UsbPortAidl.isServicePresent(null)) { + logAndPrint(Log.INFO, null, "USB HAL AIDL present"); + return new UsbPortAidl(portManager, pw); + } + + return null; + } +} 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 new file mode 100644 index 000000000000..00d0d06705fc --- /dev/null +++ b/services/usb/java/com/android/server/usb/hal/port/UsbPortHidl.java @@ -0,0 +1,471 @@ +/* + * Copyright (C) 2021 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 com.android.server.usb.hal.port; + +import static android.hardware.usb.UsbManager.USB_HAL_NOT_SUPPORTED; +import static android.hardware.usb.UsbManager.USB_HAL_V1_0; +import static android.hardware.usb.UsbManager.USB_HAL_V1_1; +import static android.hardware.usb.UsbManager.USB_HAL_V1_2; +import static android.hardware.usb.UsbManager.USB_HAL_V1_3; +import static android.hardware.usb.UsbOperationInternal.USB_OPERATION_ERROR_INTERNAL; +import static android.hardware.usb.UsbOperationInternal.USB_OPERATION_ERROR_NOT_SUPPORTED; +import static android.hardware.usb.UsbOperationInternal.USB_OPERATION_SUCCESS; +import static android.hardware.usb.UsbPortStatus.CONTAMINANT_DETECTION_NOT_SUPPORTED; +import static android.hardware.usb.UsbPortStatus.CONTAMINANT_PROTECTION_NONE; +import static android.hardware.usb.UsbPortStatus.DATA_ROLE_DEVICE; +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_ROLE_SINK; +import static android.hardware.usb.UsbPortStatus.POWER_ROLE_SOURCE; + +import static com.android.server.usb.UsbPortManager.logAndPrint; +import static com.android.server.usb.UsbPortManager.logAndPrintException; + +import android.annotation.Nullable; +import android.hardware.usb.IUsbOperationInternal; +import android.hardware.usb.UsbManager.UsbHalVersion; +import android.hardware.usb.UsbPort; +import android.hardware.usb.V1_0.IUsb; +import android.hardware.usb.V1_0.PortRoleType; +import android.hardware.usb.V1_0.Status; +import android.hardware.usb.V1_1.PortStatus_1_1; +import android.hardware.usb.V1_2.IUsbCallback; +import android.hardware.usb.V1_0.PortRole; +import android.hardware.usb.V1_2.PortStatus; +import android.hidl.manager.V1_0.IServiceManager; +import android.hidl.manager.V1_0.IServiceNotification; +import android.os.IHwBinder; +import android.os.RemoteException; +import android.util.Log; +import android.util.Slog; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.util.IndentingPrintWriter; +import com.android.server.usb.UsbPortManager; +import com.android.server.usb.hal.port.RawPortInfo; + +import java.util.ArrayList; +import java.util.NoSuchElementException; +import java.util.Objects; +/** + * + */ +public final class UsbPortHidl implements UsbPortHal { + private static final String TAG = UsbPortHidl.class.getSimpleName(); + // Cookie sent for usb hal death notification. + private static final int USB_HAL_DEATH_COOKIE = 1000; + // Proxy object for the usb hal daemon. + @GuardedBy("mLock") + private IUsb mProxy; + private UsbPortManager mPortManager; + public IndentingPrintWriter mPw; + // Mutex for all mutable shared state. + private final Object mLock = new Object(); + // Callback when the UsbPort status is changed by the kernel. + private HALCallback mHALCallback; + private boolean mSystemReady; + // Workaround since HIDL HAL versions report UsbDataEnabled status in UsbPortStatus; + private static boolean sUsbDataEnabled = true; + + public @UsbHalVersion int getUsbHalVersion() throws RemoteException { + int version; + synchronized(mLock) { + if (mProxy == null) { + throw new RemoteException("IUsb not initialized yet"); + } + if (android.hardware.usb.V1_3.IUsb.castFrom(mProxy) != null) { + version = USB_HAL_V1_3; + } else if (android.hardware.usb.V1_2.IUsb.castFrom(mProxy) != null) { + version = USB_HAL_V1_2; + } else if (android.hardware.usb.V1_1.IUsb.castFrom(mProxy) != null) { + version = USB_HAL_V1_1; + } else { + version = USB_HAL_V1_0; + } + logAndPrint(Log.INFO, null, "USB HAL HIDL version: " + version); + return version; + } + } + + final class DeathRecipient implements IHwBinder.DeathRecipient { + public IndentingPrintWriter pw; + + DeathRecipient(IndentingPrintWriter pw) { + this.pw = pw; + } + + @Override + public void serviceDied(long cookie) { + if (cookie == USB_HAL_DEATH_COOKIE) { + logAndPrint(Log.ERROR, pw, "Usb hal service died cookie: " + cookie); + synchronized (mLock) { + mProxy = null; + } + } + } + } + + final class ServiceNotification extends IServiceNotification.Stub { + @Override + public void onRegistration(String fqName, String name, boolean preexisting) { + logAndPrint(Log.INFO, null, "Usb hal service started " + fqName + " " + name); + connectToProxy(null); + } + } + + private void connectToProxy(IndentingPrintWriter pw) { + synchronized (mLock) { + if (mProxy != null) { + return; + } + + try { + mProxy = IUsb.getService(); + mProxy.linkToDeath(new DeathRecipient(pw), USB_HAL_DEATH_COOKIE); + mProxy.setCallback(mHALCallback); + mProxy.queryPortStatus(); + //updateUsbHalVersion(); + } catch (NoSuchElementException e) { + logAndPrintException(pw, "connectToProxy: usb hal service not found." + + " Did the service fail to start?", e); + } catch (RemoteException e) { + logAndPrintException(pw, "connectToProxy: usb hal service not responding", e); + } + } + } + + @Override + public void systemReady() { + mSystemReady = true; + } + + static boolean isServicePresent(IndentingPrintWriter pw) { + try { + IUsb.getService(true); + } catch (NoSuchElementException e) { + logAndPrintException(pw, "connectToProxy: usb hidl hal service not found.", e); + return false; + } catch (RemoteException e) { + logAndPrintException(pw, "IUSB hal service present but failed to get service", e); + } + + return true; + } + + public UsbPortHidl(UsbPortManager portManager, IndentingPrintWriter pw) { + mPortManager = Objects.requireNonNull(portManager); + mPw = pw; + mHALCallback = new HALCallback(null, mPortManager, this); + try { + ServiceNotification serviceNotification = new ServiceNotification(); + + boolean ret = IServiceManager.getService() + .registerForNotifications("android.hardware.usb@1.0::IUsb", + "", serviceNotification); + if (!ret) { + logAndPrint(Log.ERROR, null, + "Failed to register service start notification"); + } + } catch (RemoteException e) { + logAndPrintException(null, + "Failed to register service start notification", e); + return; + } + connectToProxy(mPw); + } + + @Override + public void enableContaminantPresenceDetection(String portName, boolean enable, + long transactionId) { + synchronized (mLock) { + if (mProxy == null) { + logAndPrint(Log.ERROR, mPw, "Proxy is null. Retry !"); + return; + } + + try { + // Oneway call into the hal. Use the castFrom method from HIDL. + android.hardware.usb.V1_2.IUsb proxy = + android.hardware.usb.V1_2.IUsb.castFrom(mProxy); + proxy.enableContaminantPresenceDetection(portName, enable); + } catch (RemoteException e) { + logAndPrintException(mPw, "Failed to set contaminant detection", e); + } catch (ClassCastException e) { + logAndPrintException(mPw, "Method only applicable to V1.2 or above implementation", + e); + } + } + } + + @Override + public void queryPortStatus(long transactionId) { + synchronized (mLock) { + if (mProxy == null) { + logAndPrint(Log.ERROR, mPw, "Proxy is null. Retry !"); + return; + } + + try { + mProxy.queryPortStatus(); + } catch (RemoteException e) { + logAndPrintException(null, "ServiceStart: Failed to query port status", e); + } + } + } + + @Override + public void switchMode(String portId, @HalUsbPortMode int newMode, long transactionId) { + synchronized (mLock) { + if (mProxy == null) { + logAndPrint(Log.ERROR, mPw, "Proxy is null. Retry !"); + return; + } + + PortRole newRole = new PortRole(); + newRole.type = PortRoleType.MODE; + newRole.role = newMode; + try { + mProxy.switchRole(portId, newRole); + } catch (RemoteException e) { + logAndPrintException(mPw, "Failed to set the USB port mode: " + + "portId=" + portId + + ", newMode=" + UsbPort.modeToString(newRole.role), e); + } + } + } + + @Override + public void switchPowerRole(String portId, @HalUsbPowerRole int newPowerRole, + long transactionId) { + synchronized (mLock) { + if (mProxy == null) { + logAndPrint(Log.ERROR, mPw, "Proxy is null. Retry !"); + return; + } + + PortRole newRole = new PortRole(); + newRole.type = PortRoleType.POWER_ROLE; + newRole.role = newPowerRole; + try { + mProxy.switchRole(portId, newRole); + } catch (RemoteException e) { + logAndPrintException(mPw, "Failed to set the USB power role: portId=" + portId + + ", newPowerRole=" + UsbPort.powerRoleToString(newRole.role), e); + } + } + } + + @Override + public void switchDataRole(String portId, @HalUsbDataRole int newDataRole, long transactionId) { + synchronized (mLock) { + if (mProxy == null) { + logAndPrint(Log.ERROR, mPw, "Proxy is null. Retry !"); + return; + } + + PortRole newRole = new PortRole(); + newRole.type = PortRoleType.DATA_ROLE; + newRole.role = newDataRole; + try { + mProxy.switchRole(portId, newRole); + } catch (RemoteException e) { + logAndPrintException(mPw, "Failed to set the USB data role: portId=" + portId + + ", newDataRole=" + UsbPort.dataRoleToString(newRole.role), e); + } + } + } + + @Override + public boolean enableUsbData(String portName, boolean enable, long transactionId, + IUsbOperationInternal callback) { + int halVersion; + + try { + halVersion = getUsbHalVersion(); + } catch (RemoteException e) { + logAndPrintException(mPw, "Failed to query USB HAL version. opID:" + + transactionId + + " portId:" + portName, e); + return false; + } + + if (halVersion != USB_HAL_V1_3) { + try { + callback.onOperationComplete(USB_OPERATION_ERROR_NOT_SUPPORTED); + } catch (RemoteException e) { + logAndPrintException(mPw, "Failed to call onOperationComplete. opID:" + + transactionId + + " portId:" + portName, e); + } + return false; + } + + boolean success; + synchronized(mLock) { + try { + android.hardware.usb.V1_3.IUsb proxy + = android.hardware.usb.V1_3.IUsb.castFrom(mProxy); + success = proxy.enableUsbDataSignal(enable); + } catch (RemoteException e) { + logAndPrintException(mPw, "Failed enableUsbData: opId:" + transactionId + + " portId=" + portName , e); + try { + callback.onOperationComplete(USB_OPERATION_ERROR_INTERNAL); + } catch (RemoteException r) { + logAndPrintException(mPw, "Failed to call onOperationComplete. opID:" + + transactionId + + " portId:" + portName, r); + } + return false; + } + } + if (success) { + sUsbDataEnabled = enable; + } + + try { + callback.onOperationComplete(success + ? USB_OPERATION_SUCCESS + : USB_OPERATION_ERROR_INTERNAL); + } catch (RemoteException r) { + logAndPrintException(mPw, "Failed to call onOperationComplete. opID:" + + transactionId + + " portId:" + portName, r); + } + return false; + } + + private static class HALCallback extends IUsbCallback.Stub { + public IndentingPrintWriter mPw; + public UsbPortManager mPortManager; + public UsbPortHidl mUsbPortHidl; + + HALCallback(IndentingPrintWriter pw, UsbPortManager portManager, UsbPortHidl usbPortHidl) { + this.mPw = pw; + this.mPortManager = portManager; + this.mUsbPortHidl = usbPortHidl; + } + + public void notifyPortStatusChange( + ArrayList<android.hardware.usb.V1_0.PortStatus> currentPortStatus, int retval) { + if (!mUsbPortHidl.mSystemReady) { + return; + } + + if (retval != Status.SUCCESS) { + UsbPortManager.logAndPrint(Log.ERROR, mPw, "port status enquiry failed"); + return; + } + + ArrayList<RawPortInfo> newPortInfo = new ArrayList<>(); + + for (android.hardware.usb.V1_0.PortStatus current : currentPortStatus) { + RawPortInfo temp = new RawPortInfo(current.portName, + current.supportedModes, CONTAMINANT_PROTECTION_NONE, + current.currentMode, + current.canChangeMode, current.currentPowerRole, + current.canChangePowerRole, + current.currentDataRole, current.canChangeDataRole, + false, CONTAMINANT_PROTECTION_NONE, + false, CONTAMINANT_DETECTION_NOT_SUPPORTED, sUsbDataEnabled); + newPortInfo.add(temp); + UsbPortManager.logAndPrint(Log.INFO, mPw, "ClientCallback V1_0: " + + current.portName); + } + + mPortManager.updatePorts(newPortInfo); + } + + + public void notifyPortStatusChange_1_1(ArrayList<PortStatus_1_1> currentPortStatus, + int retval) { + if (!mUsbPortHidl.mSystemReady) { + return; + } + + if (retval != Status.SUCCESS) { + UsbPortManager.logAndPrint(Log.ERROR, mPw, "port status enquiry failed"); + return; + } + + ArrayList<RawPortInfo> newPortInfo = new ArrayList<>(); + + int numStatus = currentPortStatus.size(); + for (int i = 0; i < numStatus; i++) { + PortStatus_1_1 current = currentPortStatus.get(i); + RawPortInfo temp = new RawPortInfo(current.status.portName, + current.supportedModes, CONTAMINANT_PROTECTION_NONE, + current.currentMode, + current.status.canChangeMode, current.status.currentPowerRole, + current.status.canChangePowerRole, + current.status.currentDataRole, current.status.canChangeDataRole, + false, CONTAMINANT_PROTECTION_NONE, + false, CONTAMINANT_DETECTION_NOT_SUPPORTED, sUsbDataEnabled); + newPortInfo.add(temp); + UsbPortManager.logAndPrint(Log.INFO, mPw, "ClientCallback V1_1: " + + current.status.portName); + } + mPortManager.updatePorts(newPortInfo); + } + + public void notifyPortStatusChange_1_2( + ArrayList<PortStatus> currentPortStatus, int retval) { + if (!mUsbPortHidl.mSystemReady) { + return; + } + + if (retval != Status.SUCCESS) { + UsbPortManager.logAndPrint(Log.ERROR, mPw, "port status enquiry failed"); + return; + } + + ArrayList<RawPortInfo> newPortInfo = new ArrayList<>(); + + int numStatus = currentPortStatus.size(); + for (int i = 0; i < numStatus; i++) { + PortStatus current = currentPortStatus.get(i); + RawPortInfo temp = new RawPortInfo(current.status_1_1.status.portName, + current.status_1_1.supportedModes, + current.supportedContaminantProtectionModes, + current.status_1_1.currentMode, + current.status_1_1.status.canChangeMode, + current.status_1_1.status.currentPowerRole, + current.status_1_1.status.canChangePowerRole, + current.status_1_1.status.currentDataRole, + current.status_1_1.status.canChangeDataRole, + current.supportsEnableContaminantPresenceProtection, + current.contaminantProtectionStatus, + current.supportsEnableContaminantPresenceDetection, + current.contaminantDetectionStatus, + sUsbDataEnabled); + newPortInfo.add(temp); + UsbPortManager.logAndPrint(Log.INFO, mPw, "ClientCallback V1_2: " + + current.status_1_1.status.portName); + } + mPortManager.updatePorts(newPortInfo); + } + + public void notifyRoleSwitchStatus(String portName, PortRole role, int retval) { + if (retval == Status.SUCCESS) { + UsbPortManager.logAndPrint(Log.INFO, mPw, portName + " role switch successful"); + } else { + UsbPortManager.logAndPrint(Log.ERROR, mPw, portName + " role switch failed"); + } + } + } +} |