Support USB V1.2 HAL

USB V1.2 HAL supports contaminant detection interface.

Bug: 119642987
Bug: 117330206
Bug: 77606903

Test: Manually tested for notification pop-up
Change-Id: I304fb4933ba8b8eaa08c32c9e8c116cba7aa6380
diff --git a/Android.bp b/Android.bp
index b68312b..8f18a46 100644
--- a/Android.bp
+++ b/Android.bp
@@ -749,6 +749,7 @@
         "android.hardware.tv.input-V1.0-java-constants",
         "android.hardware.usb-V1.0-java-constants",
         "android.hardware.usb-V1.1-java-constants",
+        "android.hardware.usb-V1.2-java-constants",
         "android.hardware.vibrator-V1.0-java",
         "android.hardware.vibrator-V1.1-java",
         "android.hardware.vibrator-V1.2-java",
diff --git a/core/java/android/hardware/usb/IUsbManager.aidl b/core/java/android/hardware/usb/IUsbManager.aidl
index edc3f94..299a00a 100644
--- a/core/java/android/hardware/usb/IUsbManager.aidl
+++ b/core/java/android/hardware/usb/IUsbManager.aidl
@@ -120,6 +120,9 @@
     /* Sets the port's current role. */
     void setPortRoles(in String portId, int powerRole, int dataRole);
 
+    /* Enable/disable contaminant detection */
+    void enableContaminantDetection(in String portId, boolean enable);
+
    /* Sets USB device connection handler. */
    void setUsbDeviceConnectionHandler(in ComponentName usbDeviceConnectionHandler);
 }
diff --git a/core/java/android/hardware/usb/ParcelableUsbPort.java b/core/java/android/hardware/usb/ParcelableUsbPort.java
index 7f7ba96..30388af 100644
--- a/core/java/android/hardware/usb/ParcelableUsbPort.java
+++ b/core/java/android/hardware/usb/ParcelableUsbPort.java
@@ -31,10 +31,21 @@
 public final class ParcelableUsbPort implements Parcelable {
     private final @NonNull String mId;
     private final int mSupportedModes;
+    private final int mSupportedContaminantProtectionModes;
+    private final boolean mSupportsEnableContaminantPresenceProtection;
+    private final boolean mSupportsEnableContaminantPresenceDetection;
 
-    private ParcelableUsbPort(@NonNull String id, int supportedModes) {
+    private ParcelableUsbPort(@NonNull String id, int supportedModes,
+            int supportedContaminantProtectionModes,
+            boolean supportsEnableContaminantPresenceProtection,
+            boolean supportsEnableContaminantPresenceDetection) {
         mId = id;
         mSupportedModes = supportedModes;
+        mSupportedContaminantProtectionModes = supportedContaminantProtectionModes;
+        mSupportsEnableContaminantPresenceProtection =
+                supportsEnableContaminantPresenceProtection;
+        mSupportsEnableContaminantPresenceDetection =
+                supportsEnableContaminantPresenceDetection;
     }
 
     /**
@@ -45,7 +56,10 @@
      * @return The parcelable version of the port
      */
     public static @NonNull ParcelableUsbPort of(@NonNull UsbPort port) {
-        return new ParcelableUsbPort(port.getId(), port.getSupportedModes());
+        return new ParcelableUsbPort(port.getId(), port.getSupportedModes(),
+                port.getSupportedContaminantProtectionModes(),
+                port.supportsEnableContaminantPresenceProtection(),
+                port.supportsEnableContaminantPresenceDetection());
     }
 
     /**
@@ -56,7 +70,9 @@
      * @return The UsbPort for this object
      */
     public @NonNull UsbPort getUsbPort(@NonNull UsbManager usbManager) {
-        return new UsbPort(usbManager, mId, mSupportedModes);
+        return new UsbPort(usbManager, mId, mSupportedModes, mSupportedContaminantProtectionModes,
+                mSupportsEnableContaminantPresenceProtection,
+                mSupportsEnableContaminantPresenceDetection);
     }
 
     @Override
@@ -68,6 +84,9 @@
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeString(mId);
         dest.writeInt(mSupportedModes);
+        dest.writeInt(mSupportedContaminantProtectionModes);
+        dest.writeBoolean(mSupportsEnableContaminantPresenceProtection);
+        dest.writeBoolean(mSupportsEnableContaminantPresenceDetection);
     }
 
     public static final Creator<ParcelableUsbPort> CREATOR =
@@ -76,7 +95,14 @@
                 public ParcelableUsbPort createFromParcel(Parcel in) {
                     String id = in.readString();
                     int supportedModes = in.readInt();
-                    return new ParcelableUsbPort(id, supportedModes);
+                    int supportedContaminantProtectionModes = in.readInt();
+                    boolean supportsEnableContaminantPresenceProtection = in.readBoolean();
+                    boolean supportsEnableContaminantPresenceDetection = in.readBoolean();
+
+                    return new ParcelableUsbPort(id, supportedModes,
+                            supportedContaminantProtectionModes,
+                            supportsEnableContaminantPresenceProtection,
+                            supportsEnableContaminantPresenceDetection);
                 }
 
                 @Override
diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java
index 6014478..eb148b9 100644
--- a/core/java/android/hardware/usb/UsbManager.java
+++ b/core/java/android/hardware/usb/UsbManager.java
@@ -854,6 +854,20 @@
     }
 
     /**
+     * Enables USB port contaminant detection algorithm.
+     *
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.MANAGE_USB)
+    void enableContaminantDetection(@NonNull UsbPort port, boolean enable) {
+        try {
+            mService.enableContaminantDetection(port.getId(), enable);
+        } catch (RemoteException e) {
+            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/UsbPort.java b/core/java/android/hardware/usb/UsbPort.java
index 37154e4..c674480 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.UsbPortStatus.CONTAMINANT_DETECTION_DETECTED;
+import static android.hardware.usb.UsbPortStatus.CONTAMINANT_DETECTION_DISABLED;
+import static android.hardware.usb.UsbPortStatus.CONTAMINANT_DETECTION_NOT_DETECTED;
+import static android.hardware.usb.UsbPortStatus.CONTAMINANT_DETECTION_NOT_SUPPORTED;
 import static android.hardware.usb.UsbPortStatus.DATA_ROLE_DEVICE;
 import static android.hardware.usb.UsbPortStatus.DATA_ROLE_HOST;
 import static android.hardware.usb.UsbPortStatus.DATA_ROLE_NONE;
@@ -48,16 +52,21 @@
     private final String mId;
     private final int mSupportedModes;
     private final UsbManager mUsbManager;
+    private final int mSupportedContaminantProtectionModes;
+    private final boolean mSupportsEnableContaminantPresenceProtection;
+    private final boolean mSupportsEnableContaminantPresenceDetection;
 
     private static final int NUM_DATA_ROLES = Constants.PortDataRole.NUM_DATA_ROLES;
-
     /**
      * Points to the first power role in the IUsb HAL.
      */
     private static final int POWER_ROLE_OFFSET = Constants.PortPowerRole.NONE;
 
     /** @hide */
-    public UsbPort(@NonNull UsbManager usbManager, @NonNull String id, int supportedModes) {
+    public UsbPort(@NonNull UsbManager usbManager, @NonNull String id, int supportedModes,
+            int supportedContaminantProtectionModes,
+            boolean supportsEnableContaminantPresenceProtection,
+            boolean supportsEnableContaminantPresenceDetection) {
         Preconditions.checkNotNull(id);
         Preconditions.checkFlagsArgument(supportedModes,
                 MODE_DFP | MODE_UFP | MODE_AUDIO_ACCESSORY | MODE_DEBUG_ACCESSORY);
@@ -65,6 +74,11 @@
         mUsbManager = usbManager;
         mId = id;
         mSupportedModes = supportedModes;
+        mSupportedContaminantProtectionModes = supportedContaminantProtectionModes;
+        mSupportsEnableContaminantPresenceProtection =
+                supportsEnableContaminantPresenceProtection;
+        mSupportsEnableContaminantPresenceDetection =
+                supportsEnableContaminantPresenceDetection;
     }
 
     /**
@@ -93,6 +107,36 @@
         return mSupportedModes;
     }
 
+   /**
+     * Gets the supported port proctection modes when the port is contaminated.
+     * <p>
+     * The actual mode of the port is decided by the hardware
+     * </p>
+     *
+     * @hide
+     */
+    public int getSupportedContaminantProtectionModes() {
+        return mSupportedContaminantProtectionModes;
+    }
+
+   /**
+     * Tells if UsbService can enable/disable contaminant presence protection.
+     *
+     * @hide
+     */
+    public boolean supportsEnableContaminantPresenceProtection() {
+        return mSupportsEnableContaminantPresenceProtection;
+    }
+
+   /**
+     * Tells if UsbService can enable/disable contaminant presence detection.
+     *
+     * @hide
+     */
+    public boolean supportsEnableContaminantPresenceDetection() {
+        return mSupportsEnableContaminantPresenceDetection;
+    }
+
     /**
      * Gets the status of this USB port.
      *
@@ -131,6 +175,12 @@
     }
 
     /**
+     * @hide
+     **/
+    public void enableContaminantDetection(boolean enable) {
+        mUsbManager.enableContaminantDetection(this, enable);
+    }
+    /**
      * Combines one power and one data role together into a unique value with
      * exactly one bit set.  This can be used to efficiently determine whether
      * a combination of roles is supported by testing whether that bit is present
@@ -206,6 +256,22 @@
     }
 
     /** @hide */
+    public static String contaminantPresenceStatusToString(int contaminantPresenceStatus) {
+        switch (contaminantPresenceStatus) {
+            case CONTAMINANT_DETECTION_NOT_SUPPORTED:
+                return "not-supported";
+            case CONTAMINANT_DETECTION_DISABLED:
+                return "disabled";
+            case CONTAMINANT_DETECTION_DETECTED:
+                return "detected";
+            case CONTAMINANT_DETECTION_NOT_DETECTED:
+                return "not detected";
+            default:
+                return Integer.toString(contaminantPresenceStatus);
+        }
+    }
+
+    /** @hide */
     public static String roleCombinationsToString(int combo) {
         StringBuilder result = new StringBuilder();
         result.append("[");
@@ -264,6 +330,11 @@
 
     @Override
     public String toString() {
-        return "UsbPort{id=" + mId + ", supportedModes=" + modeToString(mSupportedModes) + "}";
+        return "UsbPort{id=" + mId + ", supportedModes=" + modeToString(mSupportedModes)
+                + "supportedContaminantProtectionModes=" + mSupportedContaminantProtectionModes
+                + "supportsEnableContaminantPresenceProtection="
+                + mSupportsEnableContaminantPresenceProtection
+                + "supportsEnableContaminantPresenceDetection="
+                + mSupportsEnableContaminantPresenceDetection;
     }
 }
diff --git a/core/java/android/hardware/usb/UsbPortStatus.java b/core/java/android/hardware/usb/UsbPortStatus.java
index d30201a..426dba8 100644
--- a/core/java/android/hardware/usb/UsbPortStatus.java
+++ b/core/java/android/hardware/usb/UsbPortStatus.java
@@ -39,6 +39,8 @@
     private final @UsbPowerRole int mCurrentPowerRole;
     private final @UsbDataRole int mCurrentDataRole;
     private final int mSupportedRoleCombinations;
+    private final @ContaminantProtectionStatus int mContaminantProtectionStatus;
+    private final @ContaminantDetectionStatus int mContaminantDetectionStatus;
 
     /**
      * Power role: This USB port does not have a power role.
@@ -131,6 +133,93 @@
     public static final int MODE_DEBUG_ACCESSORY =
             android.hardware.usb.V1_1.Constants.PortMode_1_1.DEBUG_ACCESSORY;
 
+   /**
+     * 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;
+
+    /**
+     * Contaminant presence detection supported but disabled.
+     * @hide
+     */
+    public static final int CONTAMINANT_DETECTION_DISABLED =
+            android.hardware.usb.V1_2.Constants.ContaminantDetectionStatus.DISABLED;
+
+    /**
+     * Contaminant presence enabled but not detected.
+     * @hide
+     */
+    public static final int CONTAMINANT_DETECTION_NOT_DETECTED =
+            android.hardware.usb.V1_2.Constants.ContaminantDetectionStatus.NOT_DETECTED;
+
+    /**
+     * Contaminant presence enabled and detected.
+     * @hide
+     */
+    public static final int CONTAMINANT_DETECTION_DETECTED =
+            android.hardware.usb.V1_2.Constants.ContaminantDetectionStatus.DETECTED;
+
+    /**
+     * 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;
+
+    /**
+     * 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;
+
+    /**
+     * 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;
+
+    /**
+     * 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;
+
+    /**
+     * 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;
+
+    @IntDef(prefix = { "CONTAMINANT_DETECION_" }, flag = true, value = {
+            CONTAMINANT_DETECTION_NOT_SUPPORTED,
+            CONTAMINANT_DETECTION_DISABLED,
+            CONTAMINANT_DETECTION_NOT_DETECTED,
+            CONTAMINANT_DETECTION_DETECTED,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @interface ContaminantDetectionStatus{}
+
+    @IntDef(prefix = { "CONTAMINANT_PROTECTION_" }, flag = true, value = {
+            CONTAMINANT_PROTECTION_NONE,
+            CONTAMINANT_PROTECTION_SINK,
+            CONTAMINANT_PROTECTION_SOURCE,
+            CONTAMINANT_PROTECTION_FORCE_DISABLE,
+            CONTAMINANT_PROTECTION_DISABLED,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @interface ContaminantProtectionStatus{}
+
     @IntDef(prefix = { "MODE_" }, flag = true, value = {
             MODE_NONE,
             MODE_DFP,
@@ -142,12 +231,15 @@
     @interface UsbPortMode{}
 
     /** @hide */
-    public UsbPortStatus(int currentMode, @UsbPowerRole int currentPowerRole,
-            @UsbDataRole int currentDataRole, int supportedRoleCombinations) {
+    public UsbPortStatus(int currentMode, int currentPowerRole, int currentDataRole,
+            int supportedRoleCombinations, int contaminantProtectionStatus,
+            int contaminantDetectionStatus) {
         mCurrentMode = currentMode;
         mCurrentPowerRole = currentPowerRole;
         mCurrentDataRole = currentDataRole;
         mSupportedRoleCombinations = supportedRoleCombinations;
+        mContaminantProtectionStatus = contaminantProtectionStatus;
+        mContaminantDetectionStatus = contaminantDetectionStatus;
     }
 
     /**
@@ -212,6 +304,24 @@
         return mSupportedRoleCombinations;
     }
 
+    /**
+     * Returns contaminant detection status.
+     *
+     * @hide
+     */
+    public @ContaminantDetectionStatus int getContaminantDetectionStatus() {
+        return mContaminantDetectionStatus;
+    }
+
+    /**
+     * Returns contamiant protection status.
+     *
+     * @hide
+     */
+    public @ContaminantProtectionStatus int getContaminantProtectionStatus() {
+        return mContaminantProtectionStatus;
+    }
+
     @Override
     public String toString() {
         return "UsbPortStatus{connected=" + isConnected()
@@ -220,6 +330,10 @@
                 + ", currentDataRole=" + UsbPort.dataRoleToString(mCurrentDataRole)
                 + ", supportedRoleCombinations="
                         + UsbPort.roleCombinationsToString(mSupportedRoleCombinations)
+                + ", contaminantDetectionStatus="
+                        + getContaminantDetectionStatus()
+                + ", contaminantProtectionStatus="
+                        + getContaminantProtectionStatus()
                 + "}";
     }
 
@@ -234,6 +348,8 @@
         dest.writeInt(mCurrentPowerRole);
         dest.writeInt(mCurrentDataRole);
         dest.writeInt(mSupportedRoleCombinations);
+        dest.writeInt(mContaminantProtectionStatus);
+        dest.writeInt(mContaminantDetectionStatus);
     }
 
     public static final Parcelable.Creator<UsbPortStatus> CREATOR =
@@ -244,8 +360,11 @@
             int currentPowerRole = in.readInt();
             int currentDataRole = in.readInt();
             int supportedRoleCombinations = in.readInt();
+            int contaminantProtectionStatus = in.readInt();
+            int contaminantDetectionStatus = in.readInt();
             return new UsbPortStatus(currentMode, currentPowerRole, currentDataRole,
-                    supportedRoleCombinations);
+                    supportedRoleCombinations, contaminantProtectionStatus,
+                    contaminantDetectionStatus);
         }
 
         @Override
diff --git a/core/java/com/android/internal/usb/DumpUtils.java b/core/java/com/android/internal/usb/DumpUtils.java
index 240c2e7..3260136 100644
--- a/core/java/com/android/internal/usb/DumpUtils.java
+++ b/core/java/com/android/internal/usb/DumpUtils.java
@@ -196,6 +196,15 @@
         }
     }
 
+    private static void writeContaminantPresenceStatus(@NonNull DualDumpOutputStream dump,
+            @NonNull String idName, long id, int contaminantPresenceStatus) {
+        if (dump.isProto()) {
+            dump.write(idName, id, contaminantPresenceStatus);
+        } else {
+            dump.write(idName, id,
+                    UsbPort.contaminantPresenceStatusToString(contaminantPresenceStatus));
+        }
+    }
 
     public static void writePortStatus(@NonNull DualDumpOutputStream dump, @NonNull String idName,
             long id, @NonNull UsbPortStatus status) {
@@ -232,6 +241,10 @@
             dump.end(roleCombinationToken);
         }
 
+        writeContaminantPresenceStatus(dump, "contaminant_presence_status",
+                UsbPortStatusProto.CONTAMINANT_PRESENCE_STATUS,
+                status.getContaminantDetectionStatus());
+
         dump.end(token);
     }
 }
diff --git a/core/proto/android/service/usb.proto b/core/proto/android/service/usb.proto
index f7dcee2..00fae3d 100644
--- a/core/proto/android/service/usb.proto
+++ b/core/proto/android/service/usb.proto
@@ -245,11 +245,20 @@
         DATA_ROLE_DEVICE = 2;
     }
 
+    /* Same as android.hardware.usb.V1_2.Constants.ContaminantPresenceStatus */
+    enum ContaminantPresenceStatus {
+        CONTAMINANT_STATUS_NOT_SUPPORTED = 0;
+        CONTAMINANT_STATUS_DISABLED = 1;
+        CONTAMINANT_STATUS_NOT_DETECTED = 2;
+        CONTAMINANT_STATUS_DETECTED = 3;
+    }
+
     optional bool connected = 1;
     optional UsbPortProto.Mode current_mode = 2;
     optional PowerRole power_role = 3;
     optional DataRole data_role = 4;
     repeated UsbPortStatusRoleCombinationProto role_combinations = 5;
+    optional ContaminantPresenceStatus contaminant_presence_status = 6;
 }
 
 message UsbPortStatusRoleCombinationProto {
diff --git a/services/usb/Android.bp b/services/usb/Android.bp
index feb7b76..20855b7 100644
--- a/services/usb/Android.bp
+++ b/services/usb/Android.bp
@@ -10,6 +10,7 @@
     static_libs: [
         "android.hardware.usb-V1.0-java",
         "android.hardware.usb-V1.1-java",
+        "android.hardware.usb-V1.2-java",
         "android.hardware.usb.gadget-V1.0-java",
     ],
 }
diff --git a/services/usb/java/com/android/server/usb/UsbPortManager.java b/services/usb/java/com/android/server/usb/UsbPortManager.java
index 6f210e3..3d051d8 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.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;
@@ -35,13 +37,13 @@
 import android.hardware.usb.UsbManager;
 import android.hardware.usb.UsbPort;
 import android.hardware.usb.UsbPortStatus;
-import android.hardware.usb.V1_0.IUsb;
 import android.hardware.usb.V1_0.PortRole;
 import android.hardware.usb.V1_0.PortRoleType;
-import android.hardware.usb.V1_0.PortStatus;
 import android.hardware.usb.V1_0.Status;
-import android.hardware.usb.V1_1.IUsbCallback;
 import android.hardware.usb.V1_1.PortStatus_1_1;
+import android.hardware.usb.V1_2.IUsb;
+import android.hardware.usb.V1_2.IUsbCallback;
+import android.hardware.usb.V1_2.PortStatus;
 import android.hidl.manager.V1_0.IServiceManager;
 import android.hidl.manager.V1_0.IServiceNotification;
 import android.os.Bundle;
@@ -184,6 +186,43 @@
         }
     }
 
+    /**
+     * Enables/disables contaminant detection.
+     *
+     * @param portId port identifier.
+     * @param enable enable contaminant detection when set to true.
+     */
+    public void enableContaminantDetection(@NonNull String portId, boolean enable,
+            @NonNull IndentingPrintWriter pw) {
+        final PortInfo portInfo = mPorts.get(portId);
+        if (portInfo == null) {
+            if (pw != null) {
+                pw.println("No such USB port: " + portId);
+            }
+            return;
+        }
+
+        if (!portInfo.mUsbPort.supportsEnableContaminantPresenceDetection()) {
+            return;
+        }
+
+        if ((enable && portInfo.mUsbPortStatus.getContaminantDetectionStatus()
+                != UsbPortStatus.CONTAMINANT_DETECTION_DISABLED) || (!enable
+                && portInfo.mUsbPortStatus.getContaminantDetectionStatus()
+                == UsbPortStatus.CONTAMINANT_DETECTION_DISABLED)
+                || (portInfo.mUsbPortStatus.getContaminantDetectionStatus()
+                == UsbPortStatus.CONTAMINANT_DETECTION_NOT_SUPPORTED)) {
+            return;
+        }
+
+        try {
+            // Oneway call into the hal
+            mProxy.enableContaminantPresenceDetection(portId, enable);
+        } catch (RemoteException e) {
+            logAndPrintException(null, "Failed to set contaminant detection", e);
+        }
+    }
+
     public void setPortRoles(String portId, int newPowerRole, int newDataRole,
             IndentingPrintWriter pw) {
         synchronized (mLock) {
@@ -441,7 +480,8 @@
             this.portManager = portManager;
         }
 
-        public void notifyPortStatusChange(ArrayList<PortStatus> currentPortStatus, int retval) {
+        public void notifyPortStatusChange(
+                ArrayList<android.hardware.usb.V1_0.PortStatus> currentPortStatus, int retval) {
             if (!portManager.mSystemReady) {
                 return;
             }
@@ -453,14 +493,17 @@
 
             ArrayList<RawPortInfo> newPortInfo = new ArrayList<>();
 
-            for (PortStatus current : currentPortStatus) {
+            for (android.hardware.usb.V1_0.PortStatus current : currentPortStatus) {
                 RawPortInfo temp = new RawPortInfo(current.portName,
-                        current.supportedModes, current.currentMode,
+                        current.supportedModes, CONTAMINANT_PROTECTION_NONE,
+                        current.currentMode,
                         current.canChangeMode, current.currentPowerRole,
                         current.canChangePowerRole,
-                        current.currentDataRole, current.canChangeDataRole);
+                        current.currentDataRole, current.canChangeDataRole,
+                        false, CONTAMINANT_PROTECTION_NONE,
+                        false, CONTAMINANT_DETECTION_NOT_SUPPORTED);
                 newPortInfo.add(temp);
-                logAndPrint(Log.INFO, pw, "ClientCallback: " + current.portName);
+                logAndPrint(Log.INFO, pw, "ClientCallback V1_0: " + current.portName);
             }
 
             Message message = portManager.mHandler.obtainMessage();
@@ -485,14 +528,61 @@
 
             ArrayList<RawPortInfo> newPortInfo = new ArrayList<>();
 
-            for (PortStatus_1_1 current : currentPortStatus) {
+            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, current.currentMode,
+                        current.supportedModes, CONTAMINANT_PROTECTION_NONE,
+                        current.currentMode,
                         current.status.canChangeMode, current.status.currentPowerRole,
                         current.status.canChangePowerRole,
-                        current.status.currentDataRole, current.status.canChangeDataRole);
+                        current.status.currentDataRole, current.status.canChangeDataRole,
+                        false, CONTAMINANT_PROTECTION_NONE,
+                        false, CONTAMINANT_DETECTION_NOT_SUPPORTED);
                 newPortInfo.add(temp);
-                logAndPrint(Log.INFO, pw, "ClientCallback: " + current.status.portName);
+                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();
@@ -573,16 +663,26 @@
             for (int i = 0; i < count; i++) {
                 final RawPortInfo portInfo = mSimulatedPorts.valueAt(i);
                 addOrUpdatePortLocked(portInfo.portId, portInfo.supportedModes,
+                        portInfo.supportedContaminantProtectionModes,
                         portInfo.currentMode, portInfo.canChangeMode,
                         portInfo.currentPowerRole, portInfo.canChangePowerRole,
-                        portInfo.currentDataRole, portInfo.canChangeDataRole, pw);
+                        portInfo.currentDataRole, portInfo.canChangeDataRole,
+                        portInfo.supportsEnableContaminantPresenceProtection,
+                        portInfo.contaminantProtectionStatus,
+                        portInfo.supportsEnableContaminantPresenceDetection,
+                        portInfo.contaminantDetectionStatus, pw);
             }
         } else {
             for (RawPortInfo currentPortInfo : newPortInfo) {
                 addOrUpdatePortLocked(currentPortInfo.portId, currentPortInfo.supportedModes,
+                        currentPortInfo.supportedContaminantProtectionModes,
                         currentPortInfo.currentMode, currentPortInfo.canChangeMode,
                         currentPortInfo.currentPowerRole, currentPortInfo.canChangePowerRole,
-                        currentPortInfo.currentDataRole, currentPortInfo.canChangeDataRole, pw);
+                        currentPortInfo.currentDataRole, currentPortInfo.canChangeDataRole,
+                        currentPortInfo.supportsEnableContaminantPresenceProtection,
+                        currentPortInfo.contaminantProtectionStatus,
+                        currentPortInfo.supportsEnableContaminantPresenceDetection,
+                        currentPortInfo.contaminantDetectionStatus, pw);
             }
         }
 
@@ -608,12 +708,16 @@
         }
     }
 
-
     // Must only be called by updatePortsLocked.
     private void addOrUpdatePortLocked(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,
             IndentingPrintWriter pw) {
         // Only allow mode switch capability for dual role ports.
         // Validate that the current mode matches the supported modes we expect.
@@ -664,12 +768,15 @@
         // Update the port data structures.
         PortInfo portInfo = mPorts.get(portId);
         if (portInfo == null) {
-            portInfo = new PortInfo(mContext.getSystemService(UsbManager.class), portId,
-                    supportedModes);
+            portInfo = new PortInfo(mContext.getSystemService(UsbManager.class),
+                portId, supportedModes, supportedContaminantProtectionModes,
+                supportsEnableContaminantPresenceProtection,
+                supportsEnableContaminantPresenceDetection);
             portInfo.setStatus(currentMode, canChangeMode,
                     currentPowerRole, canChangePowerRole,
                     currentDataRole, canChangeDataRole,
-                    supportedRoleCombinations);
+                    supportedRoleCombinations, contaminantProtectionStatus,
+                    contaminantDetectionStatus);
             mPorts.put(portId, portInfo);
         } else {
             // Sanity check that ports aren't changing definition out from under us.
@@ -681,10 +788,32 @@
                         + ", current=" + UsbPort.modeToString(supportedModes));
             }
 
+            if (supportsEnableContaminantPresenceProtection
+                    != portInfo.mUsbPort.supportsEnableContaminantPresenceProtection()) {
+                logAndPrint(Log.WARN, pw,
+                        "Ignoring inconsistent supportsEnableContaminantPresenceProtection"
+                        + "USB port driver (should be immutable): "
+                        + "previous="
+                        + portInfo.mUsbPort.supportsEnableContaminantPresenceProtection()
+                        + ", current=" + supportsEnableContaminantPresenceProtection);
+            }
+
+            if (supportsEnableContaminantPresenceDetection
+                    != portInfo.mUsbPort.supportsEnableContaminantPresenceDetection()) {
+                logAndPrint(Log.WARN, pw,
+                        "Ignoring inconsistent supportsEnableContaminantPresenceDetection "
+                        + "USB port driver (should be immutable): "
+                        + "previous="
+                        + portInfo.mUsbPort.supportsEnableContaminantPresenceDetection()
+                        + ", current=" + supportsEnableContaminantPresenceDetection);
+            }
+
+
             if (portInfo.setStatus(currentMode, canChangeMode,
                     currentPowerRole, canChangePowerRole,
                     currentDataRole, canChangeDataRole,
-                    supportedRoleCombinations)) {
+                    supportedRoleCombinations, contaminantProtectionStatus,
+                    contaminantDetectionStatus)) {
                 portInfo.mDisposition = PortInfo.DISPOSITION_CHANGED;
             } else {
                 portInfo.mDisposition = PortInfo.DISPOSITION_READY;
@@ -784,8 +913,14 @@
         // 0 when port is connected. Else reports the last connected duration
         public long mLastConnectDurationMillis;
 
-        PortInfo(@NonNull UsbManager usbManager, @NonNull String portId, int supportedModes) {
-            mUsbPort = new UsbPort(usbManager, portId, supportedModes);
+        PortInfo(@NonNull UsbManager usbManager, @NonNull String portId, int supportedModes,
+                int supportedContaminantProtectionModes,
+                boolean supportsEnableContaminantPresenceDetection,
+                boolean supportsEnableContaminantPresenceProtection) {
+            mUsbPort = new UsbPort(usbManager, portId, supportedModes,
+                    supportedContaminantProtectionModes,
+                    supportsEnableContaminantPresenceDetection,
+                    supportsEnableContaminantPresenceProtection);
         }
 
         public boolean setStatus(int currentMode, boolean canChangeMode,
@@ -804,7 +939,45 @@
                     || mUsbPortStatus.getSupportedRoleCombinations()
                     != supportedRoleCombinations) {
                 mUsbPortStatus = new UsbPortStatus(currentMode, currentPowerRole, currentDataRole,
-                        supportedRoleCombinations);
+                        supportedRoleCombinations, UsbPortStatus.CONTAMINANT_PROTECTION_NONE,
+                        UsbPortStatus.CONTAMINANT_DETECTION_NOT_SUPPORTED);
+                dispositionChanged = true;
+            }
+
+            if (mUsbPortStatus.isConnected() && mConnectedAtMillis == 0) {
+                mConnectedAtMillis = SystemClock.elapsedRealtime();
+                mLastConnectDurationMillis = 0;
+            } else if (!mUsbPortStatus.isConnected() && mConnectedAtMillis != 0) {
+                mLastConnectDurationMillis = SystemClock.elapsedRealtime() - mConnectedAtMillis;
+                mConnectedAtMillis = 0;
+            }
+
+            return dispositionChanged;
+        }
+
+        public boolean setStatus(int currentMode, boolean canChangeMode,
+                int currentPowerRole, boolean canChangePowerRole,
+                int currentDataRole, boolean canChangeDataRole,
+                int supportedRoleCombinations, int contaminantProtectionStatus,
+                int contaminantDetectionStatus) {
+            boolean dispositionChanged = false;
+
+            mCanChangeMode = canChangeMode;
+            mCanChangePowerRole = canChangePowerRole;
+            mCanChangeDataRole = canChangeDataRole;
+            if (mUsbPortStatus == null
+                    || mUsbPortStatus.getCurrentMode() != currentMode
+                    || mUsbPortStatus.getCurrentPowerRole() != currentPowerRole
+                    || mUsbPortStatus.getCurrentDataRole() != currentDataRole
+                    || mUsbPortStatus.getSupportedRoleCombinations()
+                    != supportedRoleCombinations
+                    || mUsbPortStatus.getContaminantProtectionStatus()
+                    != contaminantProtectionStatus
+                    || mUsbPortStatus.getContaminantDetectionStatus()
+                    != contaminantDetectionStatus) {
+                mUsbPortStatus = new UsbPortStatus(currentMode, currentPowerRole, currentDataRole,
+                        supportedRoleCombinations, contaminantProtectionStatus,
+                        contaminantDetectionStatus);
                 dispositionChanged = true;
             }
 
@@ -855,32 +1028,54 @@
     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,
+        RawPortInfo(String portId, int supportedModes, int supportedContaminantProtectionModes,
                 int currentMode, boolean canChangeMode,
                 int currentPowerRole, boolean canChangePowerRole,
-                int currentDataRole, boolean canChangeDataRole) {
+                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;
@@ -890,35 +1085,50 @@
         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 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;
-                        return new RawPortInfo(id, supportedModes, currentMode, canChangeMode,
-                                currentPowerRole, canChangePowerRole,
-                                currentDataRole, canChangeDataRole);
-                    }
+            @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];
-                    }
-                };
+            @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 9115477..9be542f 100644
--- a/services/usb/java/com/android/server/usb/UsbService.java
+++ b/services/usb/java/com/android/server/usb/UsbService.java
@@ -554,6 +554,21 @@
     }
 
     @Override
+    public void enableContaminantDetection(String portId, boolean enable) {
+        Preconditions.checkNotNull(portId, "portId must not be null");
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
+
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            if (mPortManager != null) {
+                mPortManager.enableContaminantDetection(portId, enable, null);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    @Override
     public void setUsbDeviceConnectionHandler(ComponentName usbDeviceConnectionHandler) {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
         synchronized (mLock) {