Merge "Add CtsShimPrivUpgrade to shim.v2_apk_in_apex_upgrades"
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 9db3cdc..fd87a61 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -100,10 +100,12 @@
     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 95fc7ec..abc2b74 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -4169,8 +4169,14 @@
   }
 
   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 7f07af7..3e79f18 100644
--- a/core/java/android/hardware/usb/IUsbManager.aidl
+++ b/core/java/android/hardware/usb/IUsbManager.aidl
@@ -18,6 +18,7 @@
 
 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 @@
     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 @@
     /* 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 0000000..3f3bbf6
--- /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 964d7c1..df70bfd 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.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.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.StringJoiner;
 
 /**
@@ -516,6 +519,14 @@
     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}
@@ -556,6 +567,14 @@
     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}
      */
@@ -664,6 +683,7 @@
             USB_HAL_V1_1,
             USB_HAL_V1_2,
             USB_HAL_V1_3,
+            USB_HAL_V2_0,
     })
     public @interface UsbHalVersion {}
 
@@ -1168,8 +1188,9 @@
     /**
      * 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
@@ -1180,11 +1201,11 @@
      */
     @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;
     }
 
     /**
@@ -1270,6 +1291,41 @@
     }
 
     /**
+     * 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 0000000..9bc2b38
--- /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 274e23f..f469a3e 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_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 @@
  */
 @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 @@
      */
     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 @@
      * {@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 @@
     }
 
     /**
+     * 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 bb7aff6..bd2f9aa 100644
--- a/core/java/android/hardware/usb/UsbPortStatus.java
+++ b/core/java/android/hardware/usb/UsbPortStatus.java
@@ -19,7 +19,6 @@
 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 @@
 @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 @@
     /**
      * 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,15 +93,7 @@
     /**
      * There is currently nothing connected to this USB port.
      */
-    public static final int MODE_NONE = Constants.PortMode.NONE;
-
-    /**
-     * This USB port can act as a downstream facing port (host).
-     *
-     * <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_DFP = Constants.PortMode.DFP;
+    public static final int MODE_NONE = 0;
 
     /**
      * This USB port can act as an upstream facing port (device).
@@ -108,7 +101,15 @@
      * <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_UFP = Constants.PortMode.UFP;
+    public static final int MODE_UFP = 1 << 0;
+
+    /**
+     * This USB port can act as a downstream facing port (host).
+     *
+     * <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_DFP = 1 << 1;
 
     /**
      * This USB port can act either as an downstream facing port (host) or as
@@ -120,87 +121,76 @@
      *
      * @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 @@
     /** @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 @@
         mSupportedRoleCombinations = supportedRoleCombinations;
         mContaminantProtectionStatus = contaminantProtectionStatus;
         mContaminantDetectionStatus = contaminantDetectionStatus;
+        mUsbDataEnabled = true;
     }
 
     /**
@@ -323,6 +327,15 @@
         return mContaminantProtectionStatus;
     }
 
+    /**
+     * Returns UsbData status.
+     *
+     * @hide
+     */
+    public boolean getUsbDataStatus() {
+        return mUsbDataEnabled;
+    }
+
     @NonNull
     @Override
     public String toString() {
@@ -336,6 +349,8 @@
                         + getContaminantDetectionStatus()
                 + ", contaminantProtectionStatus="
                         + getContaminantProtectionStatus()
+                + ", usbDataEnabled="
+                        + getUsbDataStatus()
                 + "}";
     }
 
@@ -352,6 +367,7 @@
         dest.writeInt(mSupportedRoleCombinations);
         dest.writeInt(mContaminantProtectionStatus);
         dest.writeInt(mContaminantDetectionStatus);
+        dest.writeBoolean(mUsbDataEnabled);
     }
 
     public static final @NonNull Parcelable.Creator<UsbPortStatus> CREATOR =
@@ -364,9 +380,10 @@
             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/android/net/VpnProfileState.java b/core/java/android/net/VpnProfileState.java
index 0f21a9d..552a2c1 100644
--- a/core/java/android/net/VpnProfileState.java
+++ b/core/java/android/net/VpnProfileState.java
@@ -24,6 +24,7 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
 import java.util.StringJoiner;
 
 /**
@@ -176,4 +177,19 @@
         resultJoiner.add("Lockdown: " + isLockdownEnabled());
         return resultJoiner.toString();
     }
+
+    @Override
+    public boolean equals(@Nullable Object obj) {
+        if (!(obj instanceof VpnProfileState)) return false;
+        final VpnProfileState that = (VpnProfileState) obj;
+        return (getState() == that.getState()
+                && Objects.equals(getSessionId(), that.getSessionId())
+                && isAlwaysOn() == that.isAlwaysOn()
+                && isLockdownEnabled() == that.isLockdownEnabled());
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(getState(), getSessionId(), isAlwaysOn(), isLockdownEnabled());
+    }
 }
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index 4c1cc97..71b5354 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -306,6 +306,13 @@
     public static final String NAMESPACE_MEDIA_NATIVE = "media_native";
 
     /**
+     * Namespace for all Kernel Multi-Gen LRU feature.
+     *
+     * @hide
+     */
+    public static final String NAMESPACE_MGLRU_NATIVE = "mglru_native";
+
+    /**
      * Namespace for all netd related features.
      *
      * @hide
diff --git a/core/java/com/android/internal/usb/DumpUtils.java b/core/java/com/android/internal/usb/DumpUtils.java
index 3260136..744fe59 100644
--- a/core/java/com/android/internal/usb/DumpUtils.java
+++ b/core/java/com/android/internal/usb/DumpUtils.java
@@ -244,7 +244,8 @@
         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 45f8c132..cd002da 100644
--- a/core/proto/android/service/usb.proto
+++ b/core/proto/android/service/usb.proto
@@ -195,9 +195,19 @@
 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 {
@@ -253,6 +263,7 @@
     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/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index c5ac390..d01030b 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -86,6 +86,7 @@
         DeviceConfig.NAMESPACE_INTELLIGENCE_CONTENT_SUGGESTIONS,
         DeviceConfig.NAMESPACE_LMKD_NATIVE,
         DeviceConfig.NAMESPACE_MEDIA_NATIVE,
+        DeviceConfig.NAMESPACE_MGLRU_NATIVE,
         DeviceConfig.NAMESPACE_NETD_NATIVE,
         DeviceConfig.NAMESPACE_NNAPI_NATIVE,
         DeviceConfig.NAMESPACE_PROFCOLLECT_NATIVE_BOOT,
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index d9db28a..b49654e 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -2527,7 +2527,9 @@
             super(TAG);
             mProfile = profile;
             mIpSecManager = (IpSecManager) mContext.getSystemService(Context.IPSEC_SERVICE);
-            mNetworkCallback = new VpnIkev2Utils.Ikev2VpnNetworkCallback(TAG, this);
+            // Pass mExecutor into Ikev2VpnNetworkCallback and make sure that IkeV2VpnRunnerCallback
+            // will be called by the mExecutor thread.
+            mNetworkCallback = new VpnIkev2Utils.Ikev2VpnNetworkCallback(TAG, this, mExecutor);
             mSessionKey = UUID.randomUUID().toString();
         }
 
@@ -2682,73 +2684,68 @@
          * <p>The Ikev2VpnRunner will unconditionally switch to the new network, killing the old IKE
          * state in the process, and starting a new IkeSession instance.
          *
-         * <p>This method is called multiple times over the lifetime of the Ikev2VpnRunner, and is
-         * called on the ConnectivityService thread. Thus, the actual work MUST be proxied to the
-         * mExecutor thread in order to ensure consistency of the Ikev2VpnRunner fields.
+         * <p>This method MUST always be called on the mExecutor thread in order to ensure
+         * consistency of the Ikev2VpnRunner fields.
          */
         public void onDefaultNetworkChanged(@NonNull Network network) {
             Log.d(TAG, "Starting IKEv2/IPsec session on new network: " + network);
 
-            // Proxy to the Ikev2VpnRunner (single-thread) executor to ensure consistency in lieu
-            // of locking.
-            mExecutor.execute(() -> {
-                try {
-                    if (!mIsRunning) {
-                        Log.d(TAG, "onDefaultNetworkChanged after exit");
-                        return; // VPN has been shut down.
-                    }
-
-                    // Clear mInterface to prevent Ikev2VpnRunner being cleared when
-                    // interfaceRemoved() is called.
-                    mInterface = null;
-                    // Without MOBIKE, we have no way to seamlessly migrate. Close on old
-                    // (non-default) network, and start the new one.
-                    resetIkeState();
-                    mActiveNetwork = network;
-
-                    // Get Ike options from IkeTunnelConnectionParams if it's available in the
-                    // profile.
-                    final IkeTunnelConnectionParams ikeTunConnParams =
-                            mProfile.getIkeTunnelConnectionParams();
-                    final IkeSessionParams ikeSessionParams;
-                    final ChildSessionParams childSessionParams;
-                    if (ikeTunConnParams != null) {
-                        final IkeSessionParams.Builder builder = new IkeSessionParams.Builder(
-                                ikeTunConnParams.getIkeSessionParams()).setNetwork(network);
-                        ikeSessionParams = builder.build();
-                        childSessionParams = ikeTunConnParams.getTunnelModeChildSessionParams();
-                    } else {
-                        ikeSessionParams = VpnIkev2Utils.buildIkeSessionParams(
-                                mContext, mProfile, network);
-                        childSessionParams = VpnIkev2Utils.buildChildSessionParams(
-                                mProfile.getAllowedAlgorithms());
-                    }
-
-                    // TODO: Remove the need for adding two unused addresses with
-                    // IPsec tunnels.
-                    final InetAddress address = InetAddress.getLocalHost();
-                    mTunnelIface =
-                            mIpSecManager.createIpSecTunnelInterface(
-                                    address /* unused */,
-                                    address /* unused */,
-                                    network);
-                    NetdUtils.setInterfaceUp(mNetd, mTunnelIface.getInterfaceName());
-
-                    mSession = mIkev2SessionCreator.createIkeSession(
-                            mContext,
-                            ikeSessionParams,
-                            childSessionParams,
-                            mExecutor,
-                            new VpnIkev2Utils.IkeSessionCallbackImpl(
-                                    TAG, IkeV2VpnRunner.this, network),
-                            new VpnIkev2Utils.ChildSessionCallbackImpl(
-                                    TAG, IkeV2VpnRunner.this, network));
-                    Log.d(TAG, "Ike Session started for network " + network);
-                } catch (Exception e) {
-                    Log.i(TAG, "Setup failed for network " + network + ". Aborting", e);
-                    onSessionLost(network, e);
+            try {
+                if (!mIsRunning) {
+                    Log.d(TAG, "onDefaultNetworkChanged after exit");
+                    return; // VPN has been shut down.
                 }
-            });
+
+                // Clear mInterface to prevent Ikev2VpnRunner being cleared when
+                // interfaceRemoved() is called.
+                mInterface = null;
+                // Without MOBIKE, we have no way to seamlessly migrate. Close on old
+                // (non-default) network, and start the new one.
+                resetIkeState();
+                mActiveNetwork = network;
+
+                // Get Ike options from IkeTunnelConnectionParams if it's available in the
+                // profile.
+                final IkeTunnelConnectionParams ikeTunConnParams =
+                        mProfile.getIkeTunnelConnectionParams();
+                final IkeSessionParams ikeSessionParams;
+                final ChildSessionParams childSessionParams;
+                if (ikeTunConnParams != null) {
+                    final IkeSessionParams.Builder builder = new IkeSessionParams.Builder(
+                            ikeTunConnParams.getIkeSessionParams()).setNetwork(network);
+                    ikeSessionParams = builder.build();
+                    childSessionParams = ikeTunConnParams.getTunnelModeChildSessionParams();
+                } else {
+                    ikeSessionParams = VpnIkev2Utils.buildIkeSessionParams(
+                            mContext, mProfile, network);
+                    childSessionParams = VpnIkev2Utils.buildChildSessionParams(
+                            mProfile.getAllowedAlgorithms());
+                }
+
+                // TODO: Remove the need for adding two unused addresses with
+                // IPsec tunnels.
+                final InetAddress address = InetAddress.getLocalHost();
+                mTunnelIface =
+                        mIpSecManager.createIpSecTunnelInterface(
+                                address /* unused */,
+                                address /* unused */,
+                                network);
+                NetdUtils.setInterfaceUp(mNetd, mTunnelIface.getInterfaceName());
+
+                mSession = mIkev2SessionCreator.createIkeSession(
+                        mContext,
+                        ikeSessionParams,
+                        childSessionParams,
+                        mExecutor,
+                        new VpnIkev2Utils.IkeSessionCallbackImpl(
+                                TAG, IkeV2VpnRunner.this, network),
+                        new VpnIkev2Utils.ChildSessionCallbackImpl(
+                                TAG, IkeV2VpnRunner.this, network));
+                Log.d(TAG, "Ike Session started for network " + network);
+            } catch (Exception e) {
+                Log.i(TAG, "Setup failed for network " + network + ". Aborting", e);
+                onSessionLost(network, e);
+            }
         }
 
         /** Marks the state as FAILED, and disconnects. */
diff --git a/services/core/java/com/android/server/connectivity/VpnIkev2Utils.java b/services/core/java/com/android/server/connectivity/VpnIkev2Utils.java
index a0a596d..6982d60 100644
--- a/services/core/java/com/android/server/connectivity/VpnIkev2Utils.java
+++ b/services/core/java/com/android/server/connectivity/VpnIkev2Utils.java
@@ -86,6 +86,7 @@
 import java.util.Collection;
 import java.util.HashSet;
 import java.util.List;
+import java.util.concurrent.ExecutorService;
 
 /**
  * Utility class to build and convert IKEv2/IPsec parameters.
@@ -376,22 +377,25 @@
     static class Ikev2VpnNetworkCallback extends NetworkCallback {
         private final String mTag;
         private final Vpn.IkeV2VpnRunnerCallback mCallback;
+        private final ExecutorService mExecutor;
 
-        Ikev2VpnNetworkCallback(String tag, Vpn.IkeV2VpnRunnerCallback callback) {
+        Ikev2VpnNetworkCallback(String tag, Vpn.IkeV2VpnRunnerCallback callback,
+                ExecutorService executor) {
             mTag = tag;
             mCallback = callback;
+            mExecutor = executor;
         }
 
         @Override
         public void onAvailable(@NonNull Network network) {
             Log.d(mTag, "Starting IKEv2/IPsec session on new network: " + network);
-            mCallback.onDefaultNetworkChanged(network);
+            mExecutor.execute(() -> mCallback.onDefaultNetworkChanged(network));
         }
 
         @Override
         public void onLost(@NonNull Network network) {
             Log.d(mTag, "Tearing down; lost network: " + network);
-            mCallback.onSessionLost(network, null);
+            mExecutor.execute(() -> mCallback.onSessionLost(network, null));
         }
     }
 
diff --git a/services/usb/Android.bp b/services/usb/Android.bp
index 01feacd..4dc5423 100644
--- a/services/usb/Android.bp
+++ b/services/usb/Android.bp
@@ -29,6 +29,7 @@
         "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 ec28040..d472639 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_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.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.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 @@
     // 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 @@
      */
     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 @@
         }
 
         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 @@
      *
      * @param enable enable or disable USB data signaling
      */
-    public boolean enableUsbDataSignal(boolean enable) {
-        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);
-        } 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);
+    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 {
+            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(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 @@
                 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 @@
                     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 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 @@
                 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 @@
                         portInfo.supportsEnableContaminantPresenceProtection,
                         portInfo.contaminantProtectionStatus,
                         portInfo.supportsEnableContaminantPresenceDetection,
-                        portInfo.contaminantDetectionStatus, pw);
+                        portInfo.contaminantDetectionStatus,
+                        portInfo.usbDataEnabled, pw);
             }
         } else {
             for (RawPortInfo currentPortInfo : newPortInfo) {
@@ -881,7 +727,8 @@
                         currentPortInfo.supportsEnableContaminantPresenceProtection,
                         currentPortInfo.contaminantProtectionStatus,
                         currentPortInfo.supportsEnableContaminantPresenceDetection,
-                        currentPortInfo.contaminantDetectionStatus, pw);
+                        currentPortInfo.contaminantDetectionStatus,
+                        currentPortInfo.usbDataEnabled, pw);
             }
         }
 
@@ -917,6 +764,7 @@
             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 @@
                     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 @@
                     currentPowerRole, canChangePowerRole,
                     currentDataRole, canChangeDataRole,
                     supportedRoleCombinations, contaminantProtectionStatus,
-                    contaminantDetectionStatus)) {
+                    contaminantDetectionStatus, usbDataEnabled)) {
                 portInfo.mDisposition = PortInfo.DISPOSITION_CHANGED;
             } else {
                 portInfo.mDisposition = PortInfo.DISPOSITION_READY;
@@ -1141,14 +989,14 @@
         }
     }
 
-    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 @@
     /**
      * 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 @@
                     != 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 @@
                 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 @@
                     || 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 @@
                     UsbPortInfoProto.CONNECTED_AT_MILLIS, mConnectedAtMillis);
             dump.write("last_connect_duration_millis",
                     UsbPortInfoProto.LAST_CONNECT_DURATION_MILLIS, mLastConnectDurationMillis);
-
             dump.end(token);
         }
 
@@ -1304,115 +1153,4 @@
                     + ", 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 3d3538d..28227fc 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.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.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 @@
     }
 
     @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 0000000..9c6cbbd
--- /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 0000000..1efcd9c
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/hal/port/UsbPortAidl.java
@@ -0,0 +1,476 @@
+/*
+ * 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);
+            }
+        }
+
+        @Override
+        public String getInterfaceHash() {
+            return IUsbCallback.HASH;
+        }
+
+        @Override
+        public int getInterfaceVersion() {
+            return IUsbCallback.VERSION;
+        }
+    }
+}
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 0000000..e7f9bc2
--- /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 0000000..41f9fae
--- /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 0000000..00d0d06
--- /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");
+            }
+        }
+    }
+}