Merge "Import translations. DO NOT MERGE" into mnc-dev
diff --git a/cmds/svc/src/com/android/commands/svc/PowerCommand.java b/cmds/svc/src/com/android/commands/svc/PowerCommand.java
index da8586c..2754f2d 100644
--- a/cmds/svc/src/com/android/commands/svc/PowerCommand.java
+++ b/cmds/svc/src/com/android/commands/svc/PowerCommand.java
@@ -70,7 +70,7 @@
                         if (val != 0) {
                             // if the request is not to set it to false, wake up the screen so that
                             // it can stay on as requested
-                            pm.wakeUp(SystemClock.uptimeMillis());
+                            pm.wakeUp(SystemClock.uptimeMillis(), "PowerCommand", null);
                         }
                         pm.setStayOnSetting(val);
                     }
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index bf3bfae..849253b 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -233,8 +233,10 @@
     public static final int OP_READ_EXTERNAL_STORAGE = 59;
     /** @hide Write external storage. */
     public static final int OP_WRITE_EXTERNAL_STORAGE = 60;
+    /** @hide Turned on the screen. */
+    public static final int OP_TURN_SCREEN_ON = 61;
     /** @hide */
-    public static final int _NUM_OP = 61;
+    public static final int _NUM_OP = 62;
 
     /** Access to coarse location information. */
     public static final String OPSTR_COARSE_LOCATION = "android:coarse_location";
@@ -393,7 +395,8 @@
             OP_READ_CELL_BROADCASTS,
             OP_MOCK_LOCATION,
             OP_READ_EXTERNAL_STORAGE,
-            OP_WRITE_EXTERNAL_STORAGE
+            OP_WRITE_EXTERNAL_STORAGE,
+            OP_TURN_SCREEN_ON,
     };
 
     /**
@@ -461,7 +464,8 @@
             OPSTR_READ_CELL_BROADCASTS,
             OPSTR_MOCK_LOCATION,
             OPSTR_READ_EXTERNAL_STORAGE,
-            OPSTR_WRITE_EXTERNAL_STORAGE
+            OPSTR_WRITE_EXTERNAL_STORAGE,
+            null,
     };
 
     /**
@@ -528,8 +532,9 @@
             "BODY_SENSORS",
             "READ_CELL_BROADCASTS",
             "MOCK_LOCATION",
-            "OPSTR_READ_EXTERNAL_STORAGE",
-            "OPSTR_WRITE_EXTERNAL_STORAGE",
+            "READ_EXTERNAL_STORAGE",
+            "WRITE_EXTERNAL_STORAGE",
+            "TURN_ON_SCREEN",
     };
 
     /**
@@ -598,6 +603,7 @@
             null,
             Manifest.permission.READ_EXTERNAL_STORAGE,
             Manifest.permission.WRITE_EXTERNAL_STORAGE,
+            null, // no permission for turning the screen on
     };
 
     /**
@@ -666,7 +672,8 @@
             null, // READ_CELL_BROADCASTS
             null, // MOCK_LOCATION
             null, // READ_EXTERNAL_STORAGE
-            null  // WRITE_EXTERNAL_STORAGE
+            null, // WRITE_EXTERNAL_STORAGE
+            null, // TURN_ON_SCREEN
     };
 
     /**
@@ -734,7 +741,8 @@
             false, // READ_CELL_BROADCASTS
             false, // MOCK_LOCATION
             false, // READ_EXTERNAL_STORAGE
-            false  // WRITE_EXTERNAL_STORAGE
+            false, // WRITE_EXTERNAL_STORAGE
+            false, // TURN_ON_SCREEN
     };
 
     /**
@@ -801,7 +809,8 @@
             AppOpsManager.MODE_ALLOWED,
             AppOpsManager.MODE_ERRORED,  // OP_MOCK_LOCATION
             AppOpsManager.MODE_ALLOWED,
-            AppOpsManager.MODE_ALLOWED
+            AppOpsManager.MODE_ALLOWED,
+            AppOpsManager.MODE_ALLOWED,  // OP_TURN_ON_SCREEN
     };
 
     /**
@@ -872,7 +881,8 @@
             false,
             false,
             false,
-            false
+            false,
+            false,
     };
 
     /**
diff --git a/core/java/android/hardware/usb/IUsbManager.aidl b/core/java/android/hardware/usb/IUsbManager.aidl
index 0fe112c..80c7b1a 100644
--- a/core/java/android/hardware/usb/IUsbManager.aidl
+++ b/core/java/android/hardware/usb/IUsbManager.aidl
@@ -19,6 +19,8 @@
 import android.app.PendingIntent;
 import android.hardware.usb.UsbAccessory;
 import android.hardware.usb.UsbDevice;
+import android.hardware.usb.UsbPort;
+import android.hardware.usb.UsbPortStatus;
 import android.os.Bundle;
 import android.os.ParcelFileDescriptor;
 
@@ -108,4 +110,13 @@
 
     /* Clear public keys installed for secure USB debugging */
     void clearUsbDebuggingKeys();
+
+    /* Gets the list of USB ports. */
+    UsbPort[] getPorts();
+
+    /* Gets the status of the specified USB port. */
+    UsbPortStatus getPortStatus(in String portId);
+
+    /* Sets the port's current role. */
+    void setPortRoles(in String portId, int powerRole, int dataRole);
 }
diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java
index f58b9d6..c88f213 100644
--- a/core/java/android/hardware/usb/UsbManager.java
+++ b/core/java/android/hardware/usb/UsbManager.java
@@ -17,6 +17,8 @@
 
 package android.hardware.usb;
 
+import com.android.internal.util.Preconditions;
+
 import android.app.PendingIntent;
 import android.content.Context;
 import android.os.Bundle;
@@ -74,6 +76,22 @@
     public static final String ACTION_USB_STATE =
             "android.hardware.usb.action.USB_STATE";
 
+    /**
+     * Broadcast Action: A broadcast for USB port changes.
+     *
+     * This intent is sent when a USB port is added, removed, or changes state.
+     * <ul>
+     * <li> {@link #EXTRA_PORT} containing the {@link android.hardware.usb.UsbPort}
+     * for the port.
+     * <li> {@link #EXTRA_PORT_STATUS} containing the {@link android.hardware.usb.UsbPortStatus}
+     * for the port, or null if the port has been removed
+     * </ul>
+     *
+     * @hide
+     */
+    public static final String ACTION_USB_PORT_CHANGED =
+            "android.hardware.usb.action.USB_PORT_CHANGED";
+
    /**
      * Broadcast Action:  A broadcast for USB device attached event.
      *
@@ -214,6 +232,23 @@
     public static final String USB_FUNCTION_ACCESSORY = "accessory";
 
     /**
+     * Name of extra for {@link #ACTION_USB_PORT_CHANGED}
+     * containing the {@link UsbPort} object for the port.
+     *
+     * @hide
+     */
+    public static final String EXTRA_PORT = "port";
+
+    /**
+     * Name of extra for {@link #ACTION_USB_PORT_CHANGED}
+     * containing the {@link UsbPortStatus} object for the port, or null if the port
+     * was removed.
+     *
+     * @hide
+     */
+    public static final String EXTRA_PORT_STATUS = "portStatus";
+
+    /**
      * Name of extra for {@link #ACTION_USB_DEVICE_ATTACHED} and
      * {@link #ACTION_USB_DEVICE_DETACHED} broadcasts
      * containing the {@link UsbDevice} object for the device.
@@ -499,6 +534,77 @@
         return false;
     }
 
+    /**
+     * Returns a list of physical USB ports on the device.
+     * <p>
+     * This list is guaranteed to contain all dual-role USB Type C ports but it might
+     * be missing other ports depending on whether the kernel USB drivers have been
+     * updated to publish all of the device's ports through the new "dual_role_usb"
+     * device class (which supports all types of ports despite its name).
+     * </p>
+     *
+     * @return The list of USB ports, or null if none.
+     *
+     * @hide
+     */
+    public UsbPort[] getPorts() {
+        try {
+            return mService.getPorts();
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException in getPorts", e);
+        }
+        return null;
+    }
+
+    /**
+     * Gets the status of the specified USB port.
+     *
+     * @param port The port to query.
+     * @return The status of the specified USB port, or null if unknown.
+     *
+     * @hide
+     */
+    public UsbPortStatus getPortStatus(UsbPort port) {
+        Preconditions.checkNotNull(port, "port must not be null");
+
+        try {
+            return mService.getPortStatus(port.getId());
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException in getPortStatus", e);
+        }
+        return null;
+    }
+
+    /**
+     * Sets the desired role combination of the port.
+     * <p>
+     * The supported role combinations depend on what is connected to the port and may be
+     * determined by consulting
+     * {@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
+     * a {@link #ACTION_USB_PORT_CHANGED} broadcast will be sent.
+     * </p>
+     *
+     * @param powerRole The desired power role: {@link UsbPort#POWER_ROLE_SOURCE}
+     * or {@link UsbPort#POWER_ROLE_SINK}, or 0 if no power role.
+     * @param dataRole The desired data role: {@link UsbPort#DATA_ROLE_HOST}
+     * or {@link UsbPort#DATA_ROLE_DEVICE}, or 0 if no data role.
+     *
+     * @hide
+     */
+    public void setPortRoles(UsbPort port, int powerRole, int dataRole) {
+        Preconditions.checkNotNull(port, "port must not be null");
+        UsbPort.checkRoles(powerRole, dataRole);
+
+        try {
+            mService.setPortRoles(port.getId(), powerRole, dataRole);
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException in setPortRole", e);
+        }
+    }
+
     /** @hide */
     public static String addFunction(String functions, String function) {
         if ("none".equals(functions)) {
diff --git a/core/java/android/hardware/usb/UsbPort.aidl b/core/java/android/hardware/usb/UsbPort.aidl
new file mode 100644
index 0000000..b7a7920
--- /dev/null
+++ b/core/java/android/hardware/usb/UsbPort.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2015, 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;
+
+parcelable UsbPort;
diff --git a/core/java/android/hardware/usb/UsbPort.java b/core/java/android/hardware/usb/UsbPort.java
new file mode 100644
index 0000000..c9a4e9b
--- /dev/null
+++ b/core/java/android/hardware/usb/UsbPort.java
@@ -0,0 +1,238 @@
+/*
+ * Copyright (C) 2015 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 com.android.internal.util.Preconditions;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Represents a physical USB port and describes its characteristics.
+ * <p>
+ * This object is immutable.
+ * </p>
+ *
+ * @hide
+ */
+public final class UsbPort implements Parcelable {
+    private final String mId;
+    private final int mSupportedModes;
+
+    /**
+     * Mode bit: 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).
+     * </p>
+     */
+    public static final int MODE_DFP = 1 << 0;
+
+    /**
+     * Mode bit: This USB port can act as an upstream facing port (device).
+     * <p>
+     * Implies that the port supports the {@link #POWER_ROLE_SINK} and {@link #DATA_ROLE_DEVICE}
+     * combination of roles (and possibly others as well).
+     * </p>
+     */
+    public static final int MODE_UFP = 1 << 1;
+
+    /**
+     * Mode bit: This USB port can act either as an downstream facing port (host) or as
+     * an upstream facing port (device).
+     * <p>
+     * Implies that the port supports the {@link #POWER_ROLE_SOURCE} and {@link #DATA_ROLE_HOST}
+     * combination of roles and the {@link #POWER_ROLE_SINK} and {@link #DATA_ROLE_DEVICE}
+     * combination of roles (and possibly others as well).
+     * </p>
+     */
+    public static final int MODE_DUAL = MODE_DFP | MODE_UFP;
+
+    /**
+     * Power role: This USB port can act as a source (provide power).
+     */
+    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 = 2;
+
+    /**
+     * Data role: This USB port can act as a host (access data services).
+     */
+    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 = 2;
+
+    private static final int NUM_DATA_ROLES = 3;
+
+    /** @hide */
+    public UsbPort(String id, int supportedModes) {
+        mId = id;
+        mSupportedModes = supportedModes;
+    }
+
+    /**
+     * Gets the unique id of the port.
+     *
+     * @return The unique id of the port; not intended for display.
+     */
+    public String getId() {
+        return mId;
+    }
+
+    /**
+     * Gets the supported modes of the port.
+     * <p>
+     * The actual mode of the port may vary depending on what is plugged into it.
+     * </p>
+     *
+     * @return The supported modes: one of {@link #MODE_DFP}, {@link #MODE_UFP}, or
+     * {@link #MODE_DUAL}.
+     */
+    public int getSupportedModes() {
+        return mSupportedModes;
+    }
+
+    /**
+     * 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
+     * in a bit-field.
+     *
+     * @param powerRole The desired power role: {@link UsbPort#POWER_ROLE_SOURCE}
+     * or {@link UsbPort#POWER_ROLE_SINK}, or 0 if no power role.
+     * @param dataRole The desired data role: {@link UsbPort#DATA_ROLE_HOST}
+     * or {@link UsbPort#DATA_ROLE_DEVICE}, or 0 if no data role.
+     * @hide
+     */
+    public static int combineRolesAsBit(int powerRole, int dataRole) {
+        checkRoles(powerRole, dataRole);
+        final int index = powerRole * NUM_DATA_ROLES + dataRole;
+        return 1 << index;
+    }
+
+    /** @hide */
+    public static String modeToString(int mode) {
+        switch (mode) {
+            case 0:
+                return "none";
+            case MODE_DFP:
+                return "dfp";
+            case MODE_UFP:
+                return "ufp";
+            case MODE_DUAL:
+                return "dual";
+            default:
+                return Integer.toString(mode);
+        }
+    }
+
+    /** @hide */
+    public static String powerRoleToString(int role) {
+        switch (role) {
+            case 0:
+                return "no-power";
+            case POWER_ROLE_SOURCE:
+                return "source";
+            case POWER_ROLE_SINK:
+                return "sink";
+            default:
+                return Integer.toString(role);
+        }
+    }
+
+    /** @hide */
+    public static String dataRoleToString(int role) {
+        switch (role) {
+            case 0:
+                return "no-data";
+            case DATA_ROLE_HOST:
+                return "host";
+            case DATA_ROLE_DEVICE:
+                return "device";
+            default:
+                return Integer.toString(role);
+        }
+    }
+
+    /** @hide */
+    public static String roleCombinationsToString(int combo) {
+        StringBuilder result = new StringBuilder();
+        result.append("[");
+
+        boolean first = true;
+        while (combo != 0) {
+            final int index = Integer.numberOfTrailingZeros(combo);
+            combo &= ~(1 << index);
+            final int powerRole = index / NUM_DATA_ROLES;
+            final int dataRole = index % NUM_DATA_ROLES;
+            if (first) {
+                first = false;
+            } else {
+                result.append(", ");
+            }
+            result.append(powerRoleToString(powerRole));
+            result.append(':');
+            result.append(dataRoleToString(dataRole));
+        }
+
+        result.append("]");
+        return result.toString();
+    }
+
+    /** @hide */
+    public static void checkRoles(int powerRole, int dataRole) {
+        Preconditions.checkArgumentInRange(powerRole, 0, POWER_ROLE_SINK, "powerRole");
+        Preconditions.checkArgumentInRange(dataRole, 0, DATA_ROLE_DEVICE, "dataRole");
+    }
+
+    @Override
+    public String toString() {
+        return "UsbPort{id=" + mId + ", supportedModes=" + modeToString(mSupportedModes) + "}";
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(mId);
+        dest.writeInt(mSupportedModes);
+    }
+
+    public static final Parcelable.Creator<UsbPort> CREATOR =
+            new Parcelable.Creator<UsbPort>() {
+        @Override
+        public UsbPort createFromParcel(Parcel in) {
+            String id = in.readString();
+            int supportedModes = in.readInt();
+            return new UsbPort(id, supportedModes);
+        }
+
+        @Override
+        public UsbPort[] newArray(int size) {
+            return new UsbPort[size];
+        }
+    };
+}
diff --git a/core/java/android/hardware/usb/UsbPortStatus.aidl b/core/java/android/hardware/usb/UsbPortStatus.aidl
new file mode 100644
index 0000000..9a7e468
--- /dev/null
+++ b/core/java/android/hardware/usb/UsbPortStatus.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2015, 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;
+
+parcelable UsbPortStatus;
diff --git a/core/java/android/hardware/usb/UsbPortStatus.java b/core/java/android/hardware/usb/UsbPortStatus.java
new file mode 100644
index 0000000..5c0e81a
--- /dev/null
+++ b/core/java/android/hardware/usb/UsbPortStatus.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2015 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.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Describes the status of a USB port.
+ * <p>
+ * This object is immutable.
+ * </p>
+ *
+ * @hide
+ */
+public final class UsbPortStatus implements Parcelable {
+    private final int mCurrentMode;
+    private final int mCurrentPowerRole;
+    private final int mCurrentDataRole;
+    private final int mSupportedRoleCombinations;
+
+    /** @hide */
+    public UsbPortStatus(int currentMode, int currentPowerRole, int currentDataRole,
+            int supportedRoleCombinations) {
+        mCurrentMode = currentMode;
+        mCurrentPowerRole = currentPowerRole;
+        mCurrentDataRole = currentDataRole;
+        mSupportedRoleCombinations = supportedRoleCombinations;
+    }
+
+    /**
+     * Returns true if there is anything connected to the port.
+     *
+     * @return True if there is anything connected to the port.
+     */
+    public boolean isConnected() {
+        return mCurrentMode != 0;
+    }
+
+    /**
+     * Gets the current mode of the port.
+     *
+     * @return The current mode: {@link UsbPort#MODE_DFP}, {@link UsbPort#MODE_UFP},
+     * or 0 if nothing is connected.
+     */
+    public int getCurrentMode() {
+        return mCurrentMode;
+    }
+
+    /**
+     * Gets the current power role of the port.
+     *
+     * @return The current power role: {@link UsbPort#POWER_ROLE_SOURCE},
+     * {@link UsbPort#POWER_ROLE_SINK}, or 0 if nothing is connected.
+     */
+    public int getCurrentPowerRole() {
+        return mCurrentPowerRole;
+    }
+
+    /**
+     * Gets the current data role of the port.
+     *
+     * @return The current data role: {@link UsbPort#DATA_ROLE_HOST},
+     * {@link UsbPort#DATA_ROLE_DEVICE}, or 0 if nothing is connected.
+     */
+    public int getCurrentDataRole() {
+        return mCurrentDataRole;
+    }
+
+    /**
+     * Returns true if the specified power and data role combination is supported
+     * given what is currently connected to the port.
+     *
+     * @param powerRole The power role to check: {@link UsbPort#POWER_ROLE_SOURCE}
+     * or {@link UsbPort#POWER_ROLE_SINK}, or 0 if no power role.
+     * @param dataRole The data role to check: either {@link UsbPort#DATA_ROLE_HOST}
+     * or {@link UsbPort#DATA_ROLE_DEVICE}, or 0 if no data role.
+     */
+    public boolean isRoleCombinationSupported(int powerRole, int dataRole) {
+        return (mSupportedRoleCombinations &
+                UsbPort.combineRolesAsBit(powerRole, dataRole)) != 0;
+    }
+
+    /** @hide */
+    public int getSupportedRoleCombinations() {
+        return mSupportedRoleCombinations;
+    }
+
+    @Override
+    public String toString() {
+        return "UsbPortStatus{connected=" + isConnected()
+                + ", currentMode=" + UsbPort.modeToString(mCurrentMode)
+                + ", currentPowerRole=" + UsbPort.powerRoleToString(mCurrentPowerRole)
+                + ", currentDataRole=" + UsbPort.dataRoleToString(mCurrentDataRole)
+                + ", supportedRoleCombinations="
+                        + UsbPort.roleCombinationsToString(mSupportedRoleCombinations)
+                + "}";
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mCurrentMode);
+        dest.writeInt(mCurrentPowerRole);
+        dest.writeInt(mCurrentDataRole);
+        dest.writeInt(mSupportedRoleCombinations);
+    }
+
+    public static final Parcelable.Creator<UsbPortStatus> CREATOR =
+            new Parcelable.Creator<UsbPortStatus>() {
+        @Override
+        public UsbPortStatus createFromParcel(Parcel in) {
+            int currentMode = in.readInt();
+            int currentPowerRole = in.readInt();
+            int currentDataRole = in.readInt();
+            int supportedRoleCombinations = in.readInt();
+            return new UsbPortStatus(currentMode, currentPowerRole, currentDataRole,
+                    supportedRoleCombinations);
+        }
+
+        @Override
+        public UsbPortStatus[] newArray(int size) {
+            return new UsbPortStatus[size];
+        }
+    };
+}
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 452e4d5..ecb7f5a 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -1195,9 +1195,11 @@
         public static final int EVENT_PACKAGE_ACTIVE = 0x0010;
         // Event for a package being on the temporary whitelist.
         public static final int EVENT_TEMP_WHITELIST = 0x0011;
+        // Event for the screen waking up.
+        public static final int EVENT_SCREEN_WAKE_UP = 0x0012;
 
         // Number of event types.
-        public static final int EVENT_COUNT = 0x0012;
+        public static final int EVENT_COUNT = 0x0013;
         // Mask to extract out only the type part of the event.
         public static final int EVENT_TYPE_MASK = ~(EVENT_FLAG_START|EVENT_FLAG_FINISH);
 
@@ -1858,12 +1860,14 @@
 
     public static final String[] HISTORY_EVENT_NAMES = new String[] {
             "null", "proc", "fg", "top", "sync", "wake_lock_in", "job", "user", "userfg", "conn",
-            "active", "pkginst", "pkgunin", "alarm", "stats", "inactive", "active", "tmpwhitelist"
+            "active", "pkginst", "pkgunin", "alarm", "stats", "inactive", "active", "tmpwhitelist",
+            "screenwake",
     };
 
     public static final String[] HISTORY_EVENT_CHECKIN_NAMES = new String[] {
             "Enl", "Epr", "Efg", "Etp", "Esy", "Ewl", "Ejb", "Eur", "Euf", "Ecn",
-            "Eac", "Epi", "Epu", "Eal", "Est", "Eai", "Eaa", "Etw"
+            "Eac", "Epi", "Epu", "Eal", "Est", "Eai", "Eaa", "Etw",
+            "Esw",
     };
 
     /**
diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl
index 804d3d0..0f37ac7 100644
--- a/core/java/android/os/IPowerManager.aidl
+++ b/core/java/android/os/IPowerManager.aidl
@@ -37,7 +37,7 @@
     boolean isWakeLockLevelSupported(int level);
 
     void userActivity(long time, int event, int flags);
-    void wakeUp(long time);
+    void wakeUp(long time, String reason, String opPackageName);
     void goToSleep(long time, int reason, int flags);
     void nap(long time);
     boolean isInteractive();
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 6ef1cd0..9a1a03e 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -658,7 +658,17 @@
      */
     public void wakeUp(long time) {
         try {
-            mService.wakeUp(time);
+            mService.wakeUp(time, "wakeUp", mContext.getOpPackageName());
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * @hide
+     */
+    public void wakeUp(long time, String reason) {
+        try {
+            mService.wakeUp(time, reason, mContext.getOpPackageName());
         } catch (RemoteException e) {
         }
     }
diff --git a/core/java/android/service/dreams/Sandman.java b/core/java/android/service/dreams/Sandman.java
index 5f5b079..eeb340b 100644
--- a/core/java/android/service/dreams/Sandman.java
+++ b/core/java/android/service/dreams/Sandman.java
@@ -92,7 +92,8 @@
                     // be awake by the time this happens.  Otherwise the dream may not start.
                     PowerManager powerManager =
                             (PowerManager)context.getSystemService(Context.POWER_SERVICE);
-                    powerManager.wakeUp(SystemClock.uptimeMillis());
+                    powerManager.wakeUp(SystemClock.uptimeMillis(),
+                            "android.service.dreams:DREAM");
                 } else {
                     Slog.i(TAG, "Activating dream by user request.");
                 }
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index 6f0cec6..3cddbf6 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -83,6 +83,7 @@
     void noteScreenState(int state);
     void noteScreenBrightness(int brightness);
     void noteUserActivity(int uid, int event);
+    void noteWakeUp(String reason, int reasonUid);
     void noteInteractive(boolean interactive);
     void noteConnectivityChanged(int type, String extra);
     void noteMobileRadioPowerState(int powerState, long timestampNs);
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index ae2cbad..60f47d6 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -3122,6 +3122,13 @@
         }
     }
 
+    public void noteWakeUpLocked(String reason, int reasonUid) {
+        final long elapsedRealtime = SystemClock.elapsedRealtime();
+        final long uptime = SystemClock.uptimeMillis();
+        addHistoryEventLocked(elapsedRealtime, uptime, HistoryItem.EVENT_SCREEN_WAKE_UP,
+                reason, reasonUid);
+    }
+
     public void noteInteractiveLocked(boolean interactive) {
         if (mInteractive != interactive) {
             final long elapsedRealtime = SystemClock.elapsedRealtime();
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index d3117b9..062ae27 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -186,6 +186,7 @@
     <protected-broadcast android:name="android.hardware.display.action.WIFI_DISPLAY_STATUS_CHANGED" />
 
     <protected-broadcast android:name="android.hardware.usb.action.USB_STATE" />
+    <protected-broadcast android:name="android.hardware.usb.action.USB_PORT_CHANGED" />
     <protected-broadcast android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED" />
     <protected-broadcast android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED" />
     <protected-broadcast android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
diff --git a/core/res/res/values-az-rAZ-watch/strings.xml b/core/res/res/values-az-rAZ-watch/strings.xml
new file mode 100644
index 0000000..7e4a762
--- /dev/null
+++ b/core/res/res/values-az-rAZ-watch/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/* //device/apps/common/assets/res/any/strings.xml
+**
+** Copyright 2015, 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="android_upgrading_apk" msgid="1090732262010398759">"Tətbiq <xliff:g id="NUMBER_0">%1$d</xliff:g>/<xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
+</resources>
diff --git a/core/res/res/values-eu-rES/strings.xml b/core/res/res/values-eu-rES/strings.xml
index cc23637..960bc56 100644
--- a/core/res/res/values-eu-rES/strings.xml
+++ b/core/res/res/values-eu-rES/strings.xml
@@ -355,7 +355,7 @@
     <string name="permlab_callPhone" msgid="3925836347681847954">"deitu zuzenean telefono-zenbakietara"</string>
     <string name="permdesc_callPhone" msgid="3740797576113760827">"Telefono-zenbakietara zuk esku hartu gabe deitzeko baimena ematen die aplikazioei. Horrela, ustekabeko gastuak edo deiak eragin daitezke. Aplikazio gaiztoek erabil dezakete zuk berretsi gabeko deiak eginda gastuak eragiteko."</string>
     <string name="permlab_accessImsCallService" msgid="3574943847181793918">"Atzitu IMS dei-zerbitzua"</string>
-    <string name="permdesc_accessImsCallService" msgid="8992884015198298775">"Zuk ezer egin beharrik gabe deiak egiteko IMS zerbitzua erabiltzea baimentzen dio aplikazioari."</string>
+    <string name="permdesc_accessImsCallService" msgid="8992884015198298775">"Zuk ezer egin beharrik gabe deiak egiteko IMS zerbitzua erabiltzea baimentzen die aplikazioei."</string>
     <string name="permlab_readPhoneState" msgid="9178228524507610486">"telefonoaren egoera eta identitatea irakurtzea"</string>
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"Gailuaren telefono-eginbideak atzitzeko baimena ematen die aplikazioei. Baimen horrek aplikazioari telefono-zenbakia eta gailu IDak zein diren, deirik aktibo dagoen eta deia zer zenbakirekin konektatuta dagoen zehazteko baimena ematen die aplikazioei."</string>
     <string name="permlab_wakeLock" product="tablet" msgid="1531731435011495015">"eragotzi tableta inaktibo ezartzea"</string>
diff --git a/core/res/res/values-mcc310-mnc260-az-rAZ/strings.xml b/core/res/res/values-mcc310-mnc260-az-rAZ/strings.xml
new file mode 100644
index 0000000..32d21c5
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc260-az-rAZ/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+** Copyright 2015, 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 my 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.
+*/
+ -->
+
+<!--  These resources are around just to allow their values to be customized
+     for different hardware and product builds.  -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+  <string-array name="wfcOperatorErrorAlertMessages">
+    <item msgid="7239039348648848288">"Wi-Fi üzərindən zəng etmək və mesaj göndərmək üçün ilk öncə operatordan bu xidməti ayarlamağı tələb edin. Sonra Ayarlardan Wi-Fi çağrısını aktivləşdirin."</item>
+  </string-array>
+  <string-array name="wfcOperatorErrorNotificationMessages">
+    <item msgid="483847327467331298">"Operatorla qeydiyyatdan keçin"</item>
+  </string-array>
+    <string name="wfcSpnFormat" msgid="4982938551498609442">"%s Wi-Fi Zəngi"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc260-de/strings.xml b/core/res/res/values-mcc310-mnc260-de/strings.xml
index f357bb6..3994bba 100644
--- a/core/res/res/values-mcc310-mnc260-de/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-de/strings.xml
@@ -23,10 +23,10 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
   <string-array name="wfcOperatorErrorAlertMessages">
-    <item msgid="7239039348648848288">"Um über WLAN Anrufe durchführen und Nachrichten senden zu können, bitten Sie zuerst Ihren Mobilfunkanbieter, diesen Dienst einzurichten. Aktivieren Sie WLAN-Anrufe dann erneut über die Einstellungen."</item>
+    <item msgid="7239039348648848288">"Um über WLAN telefonieren und Nachrichten senden zu können, bitten Sie zuerst Ihren Mobilfunkanbieter, diesen Dienst einzurichten. Aktivieren Sie die Option \"Anrufe über WLAN\" dann erneut über die Einstellungen."</item>
   </string-array>
   <string-array name="wfcOperatorErrorNotificationMessages">
     <item msgid="483847327467331298">"Registrieren Sie sich bei Ihrem Mobilfunkanbieter."</item>
   </string-array>
-    <string name="wfcSpnFormat" msgid="4982938551498609442">"%s WLAN-Anrufe"</string>
+    <string name="wfcSpnFormat" msgid="4982938551498609442">"%s Anrufe über WLAN"</string>
 </resources>
diff --git a/core/res/res/values-mcc310-mnc260-kn-rIN/strings.xml b/core/res/res/values-mcc310-mnc260-kn-rIN/strings.xml
index f24bed0..0a9d58d 100644
--- a/core/res/res/values-mcc310-mnc260-kn-rIN/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-kn-rIN/strings.xml
@@ -23,10 +23,10 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
   <string-array name="wfcOperatorErrorAlertMessages">
-    <item msgid="7239039348648848288">"ವೈ-ಫೈ ಬಳಸಿಕೊಂಡು ಕರೆ ಮಾಡಲು ಮತ್ತು ಸಂದೇಶಗಳನ್ನು ಕಳುಹಿಸಲು, ಮೊದಲು ಈ ಸಾಧನವನ್ನು ಹೊಂದಿಸಲು ನಿಮ್ಮ ವಾಹಕವನ್ನು ಕೇಳಿ. ತದನಂತರ ಸೆಟ್ಟಿಂಗ್‌ಗಳಲ್ಲಿ ಮತ್ತೆ ವೈ-ಫೈ ಆನ್‌ ಮಾಡಿ."</item>
+    <item msgid="7239039348648848288">"Wi-Fi ಬಳಸಿಕೊಂಡು ಕರೆ ಮಾಡಲು ಮತ್ತು ಸಂದೇಶಗಳನ್ನು ಕಳುಹಿಸಲು, ಮೊದಲು ಈ ಸಾಧನವನ್ನು ಹೊಂದಿಸಲು ನಿಮ್ಮ ವಾಹಕವನ್ನು ಕೇಳಿ. ತದನಂತರ ಸೆಟ್ಟಿಂಗ್‌ಗಳಲ್ಲಿ ಮತ್ತೆ Wi-Fi ಆನ್‌ ಮಾಡಿ."</item>
   </string-array>
   <string-array name="wfcOperatorErrorNotificationMessages">
     <item msgid="483847327467331298">"ನಿಮ್ಮ ವಾಹಕದಲ್ಲಿ ನೋಂದಾಯಿಸಿಕೊಳ್ಳಿ"</item>
   </string-array>
-    <string name="wfcSpnFormat" msgid="4982938551498609442">"%s ವೈ-ಫೈ ಕರೆ ಮಾಡುವಿಕೆ"</string>
+    <string name="wfcSpnFormat" msgid="4982938551498609442">"%s Wi-Fi ಕರೆ ಮಾಡುವಿಕೆ"</string>
 </resources>
diff --git a/core/res/res/values-mcc310-mnc260-ml-rIN/strings.xml b/core/res/res/values-mcc310-mnc260-ml-rIN/strings.xml
index 764b792..a94680d 100644
--- a/core/res/res/values-mcc310-mnc260-ml-rIN/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-ml-rIN/strings.xml
@@ -23,10 +23,10 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
   <string-array name="wfcOperatorErrorAlertMessages">
-    <item msgid="7239039348648848288">"Wi-Fi വഴി കോളുകൾ വിളിക്കാനും സന്ദേശങ്ങൾ അയയ്‌ക്കാനും ആദ്യം നിങ്ങളുടെ കാരിയറോട് ഈ സേവനം സജ്ജമാക്കാൻ ആവശ്യപ്പെടുക. ക്രമീകരണത്തിൽ നിന്ന് വീണ്ടും Wi-Fi കോളിംഗ് ഓണാക്കുക."</item>
+    <item msgid="7239039348648848288">"വൈഫൈ വഴി കോളുകൾ വിളിക്കാനും സന്ദേശങ്ങൾ അയയ്‌ക്കാനും ആദ്യം നിങ്ങളുടെ കാരിയറോട് ഈ സേവനം സജ്ജമാക്കാൻ ആവശ്യപ്പെടുക. ക്രമീകരണത്തിൽ നിന്ന് വീണ്ടും വൈഫൈ കോളിംഗ് ഓണാക്കുക."</item>
   </string-array>
   <string-array name="wfcOperatorErrorNotificationMessages">
     <item msgid="483847327467331298">"നിങ്ങളുടെ കാരിയറിൽ രജിസ്റ്റർ ചെയ്യുക"</item>
   </string-array>
-    <string name="wfcSpnFormat" msgid="4982938551498609442">"%s Wi-Fi കോളിംഗ്"</string>
+    <string name="wfcSpnFormat" msgid="4982938551498609442">"%s വൈഫൈ കോളിംഗ്"</string>
 </resources>
diff --git a/packages/DefaultContainerService/AndroidManifest.xml b/packages/DefaultContainerService/AndroidManifest.xml
index 6a72d83..e67c554 100644
--- a/packages/DefaultContainerService/AndroidManifest.xml
+++ b/packages/DefaultContainerService/AndroidManifest.xml
@@ -9,6 +9,7 @@
          view storage for all users -->
     <uses-permission android:name="android.permission.WRITE_MEDIA_STORAGE" />
     <uses-permission android:name="android.permission.ACCESS_CACHE_FILESYSTEM" />
+    <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
 
     <application android:label="@string/service_name"
                  android:allowBackup="false">
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
index c541bca..a57bcc6 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
@@ -720,14 +720,15 @@
 
         if (mState.action == ACTION_GET_CONTENT) {
             intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
-        } else if (mState.action == ACTION_OPEN_TREE ||
-                   mState.action == ACTION_OPEN_COPY_DESTINATION) {
+        } else if (mState.action == ACTION_OPEN_TREE) {
             intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION
                     | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
                     | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
                     | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION);
-            // TODO: Move passing the stack to the separate ACTION_COPY action once it's implemented.
-            intent.putExtra(CopyService.EXTRA_STACK, (Parcelable)mState.stack);
+        } else if (mState.action == ACTION_OPEN_COPY_DESTINATION) {
+            // Picking a copy destination is only used internally by us, so we
+            // don't need to extend permissions to the caller.
+            intent.putExtra(CopyService.EXTRA_STACK, (Parcelable) mState.stack);
         } else {
             intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION
                     | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
diff --git a/packages/SettingsProvider/res/values-pt-rPT/strings.xml b/packages/SettingsProvider/res/values-pt-rPT/strings.xml
index 6bd62e3..c7dc9e6 100644
--- a/packages/SettingsProvider/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsProvider/res/values-pt-rPT/strings.xml
@@ -19,5 +19,5 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="app_label" msgid="4567566098528588863">"Armazenamento de Definições"</string>
+    <string name="app_label" msgid="4567566098528588863">"Armazenamento de definições"</string>
 </resources>
diff --git a/packages/SettingsProvider/res/values-vi/strings.xml b/packages/SettingsProvider/res/values-vi/strings.xml
index 504479d..015fbfd 100644
--- a/packages/SettingsProvider/res/values-vi/strings.xml
+++ b/packages/SettingsProvider/res/values-vi/strings.xml
@@ -19,5 +19,5 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="app_label" msgid="4567566098528588863">"Lưu trữ cài đặt"</string>
+    <string name="app_label" msgid="4567566098528588863">"Lưu trữ bộ nhớ"</string>
 </resources>
diff --git a/packages/Shell/src/com/android/shell/BugreportReceiver.java b/packages/Shell/src/com/android/shell/BugreportReceiver.java
index 6278650..d83b516 100644
--- a/packages/Shell/src/com/android/shell/BugreportReceiver.java
+++ b/packages/Shell/src/com/android/shell/BugreportReceiver.java
@@ -215,6 +215,7 @@
             ZipOutputStream zos = new ZipOutputStream(
                 new BufferedOutputStream(new FileOutputStream(bugreportZippedFile)))) {
             ZipEntry entry = new ZipEntry(bugreportFile.getName());
+            entry.setTime(bugreportFile.lastModified());
             zos.putNextEntry(entry);
             int totalBytes = Streams.copy(is, zos);
             Log.v(TAG, "size of original bugreport: " + totalBytes + " bytes");
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 155f5ea..3210a24 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -583,7 +583,7 @@
     <dimen name="managed_profile_toast_padding">4dp</dimen>
 
     <!-- Thickness of the assist disclosure beams -->
-    <dimen name="assist_disclosure_thickness">3dp</dimen>
+    <dimen name="assist_disclosure_thickness">2.5dp</dimen>
 
     <!-- Thickness of the shadows of the assist disclosure beams -->
     <dimen name="assist_disclosure_shadow_thickness">1.5dp</dimen>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 88aa071..1e78f66 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -3907,7 +3907,7 @@
     public void wakeUpIfDozing(long time, MotionEvent event) {
         if (mDozing && mDozeScrimController.isPulsing()) {
             PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
-            pm.wakeUp(time);
+            pm.wakeUp(time, "com.android.systemui:NODOZE");
             mScreenOnComingFromTouch = true;
             mScreenOnTouchLocation = new PointF(event.getX(), event.getY());
             mNotificationPanel.setTouchDisabled(false);
diff --git a/services/core/java/com/android/server/DockObserver.java b/services/core/java/com/android/server/DockObserver.java
index 41ce25d..5388f10 100644
--- a/services/core/java/com/android/server/DockObserver.java
+++ b/services/core/java/com/android/server/DockObserver.java
@@ -134,7 +134,8 @@
                 if (mAllowTheaterModeWakeFromDock
                         || Settings.Global.getInt(getContext().getContentResolver(),
                             Settings.Global.THEATER_MODE_ON, 0) == 0) {
-                    mPowerManager.wakeUp(SystemClock.uptimeMillis());
+                    mPowerManager.wakeUp(SystemClock.uptimeMillis(),
+                            "android.server:DOCK");
                 }
                 updateLocked();
             }
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 13b75ab..4b0b924 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -493,6 +493,13 @@
         }
     }
     
+    public void noteWakeUp(String reason, int reasonUid) {
+        enforceCallingPermission();
+        synchronized (mStats) {
+            mStats.noteWakeUpLocked(reason, reasonUid);
+        }
+    }
+
     public void noteInteractive(boolean interactive) {
         enforceCallingPermission();
         synchronized (mStats) {
diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java
index 458928f..8813a61 100644
--- a/services/core/java/com/android/server/dreams/DreamManagerService.java
+++ b/services/core/java/com/android/server/dreams/DreamManagerService.java
@@ -648,7 +648,8 @@
                 if (mCurrentDreamName != null && mCurrentDreamCanDoze
                         && !mCurrentDreamName.equals(getDozeComponent())) {
                     // May have updated the doze component, wake up
-                    mPowerManager.wakeUp(SystemClock.uptimeMillis());
+                    mPowerManager.wakeUp(SystemClock.uptimeMillis(),
+                            "android.server.dreams:SYSPROP");
                 }
             }
         }
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java
index 4d8d105..a71dfcd 100644
--- a/services/core/java/com/android/server/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java
@@ -565,7 +565,8 @@
             // For fingerprint devices that support touch-to-wake, this will ensure the device
             // wakes up and turns the screen on when fingerprint is authenticated.
             if (mIsKeyguard && authenticated) {
-                mPowerManager.wakeUp(SystemClock.uptimeMillis());
+                mPowerManager.wakeUp(SystemClock.uptimeMillis(),
+                        "android.server.fingerprint:AUTH");
             }
             return result;
         }
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index c37f619..cfc5f7d 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -1969,7 +1969,7 @@
     void wakeUp() {
         assertRunOnServiceThread();
         mWakeUpMessageReceived = true;
-        mPowerManager.wakeUp(SystemClock.uptimeMillis());
+        mPowerManager.wakeUp(SystemClock.uptimeMillis(), "android.server.hdmi:WAKE");
         // PowerManger will send the broadcast Intent.ACTION_SCREEN_ON and after this gets
         // the intent, the sequence will continue at onWakeUp().
     }
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 9e41f70..978ed51 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -753,7 +753,8 @@
             synchronized (mLock) {
                 if (shouldEnableWakeGestureLp()) {
                     performHapticFeedbackLw(null, HapticFeedbackConstants.VIRTUAL_KEY, false);
-                    wakeUp(SystemClock.uptimeMillis(), mAllowTheaterModeWakeFromWakeGesture);
+                    wakeUp(SystemClock.uptimeMillis(), mAllowTheaterModeWakeFromWakeGesture,
+                            "android.policy:GESTURE");
                 }
             }
         }
@@ -4691,7 +4692,8 @@
         updateRotation(true);
 
         if (lidOpen) {
-            wakeUp(SystemClock.uptimeMillis(), mAllowTheaterModeWakeFromLidSwitch);
+            wakeUp(SystemClock.uptimeMillis(), mAllowTheaterModeWakeFromLidSwitch,
+                    "android.policy:LID");
         } else if (!mLidControlsSleep) {
             mPowerManager.userActivity(SystemClock.uptimeMillis(), false);
         }
@@ -4713,7 +4715,8 @@
             } else {
                 intent = new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA);
             }
-            wakeUp(whenNanos / 1000000, mAllowTheaterModeWakeFromCameraLens);
+            wakeUp(whenNanos / 1000000, mAllowTheaterModeWakeFromCameraLens,
+                    "android.policy:CAMERA_COVER");
             startActivityAsUser(intent, UserHandle.CURRENT_OR_SELF);
         }
         mCameraLensCoverState = lensCoverState;
@@ -4892,7 +4895,7 @@
         if (isValidGlobalKey(keyCode)
                 && mGlobalKeyManager.shouldHandleGlobalKey(keyCode, event)) {
             if (isWakeKey) {
-                wakeUp(event.getEventTime(), mAllowTheaterModeWakeFromKey);
+                wakeUp(event.getEventTime(), mAllowTheaterModeWakeFromKey, "android.policy:KEY");
             }
             return result;
         }
@@ -5123,7 +5126,7 @@
         }
 
         if (isWakeKey) {
-            wakeUp(event.getEventTime(), mAllowTheaterModeWakeFromKey);
+            wakeUp(event.getEventTime(), mAllowTheaterModeWakeFromKey, "android.policy:KEY");
         }
 
         return result;
@@ -5184,7 +5187,8 @@
     @Override
     public int interceptMotionBeforeQueueingNonInteractive(long whenNanos, int policyFlags) {
         if ((policyFlags & FLAG_WAKE) != 0) {
-            if (wakeUp(whenNanos / 1000000, mAllowTheaterModeWakeFromMotion)) {
+            if (wakeUp(whenNanos / 1000000, mAllowTheaterModeWakeFromMotion,
+                    "android.policy:MOTION")) {
                 return 0;
             }
         }
@@ -5197,7 +5201,8 @@
         // there will be no dream to intercept the touch and wake into ambient.  The device should
         // wake up in this case.
         if (isTheaterModeEnabled() && (policyFlags & FLAG_WAKE) != 0) {
-            wakeUp(whenNanos / 1000000, mAllowTheaterModeWakeFromMotionWhenNotDreaming);
+            wakeUp(whenNanos / 1000000, mAllowTheaterModeWakeFromMotionWhenNotDreaming,
+                    "android.policy:MOTION");
         }
 
         return 0;
@@ -5493,10 +5498,10 @@
     }
 
     private void wakeUpFromPowerKey(long eventTime) {
-        wakeUp(eventTime, mAllowTheaterModeWakeFromPowerKey);
+        wakeUp(eventTime, mAllowTheaterModeWakeFromPowerKey, "android.policy:POWER");
     }
 
-    private boolean wakeUp(long wakeTime, boolean wakeInTheaterMode) {
+    private boolean wakeUp(long wakeTime, boolean wakeInTheaterMode, String reason) {
         final boolean theaterModeEnabled = isTheaterModeEnabled();
         if (!wakeInTheaterMode && theaterModeEnabled) {
             return false;
@@ -5507,7 +5512,7 @@
                     Settings.Global.THEATER_MODE_ON, 0);
         }
 
-        mPowerManager.wakeUp(wakeTime);
+        mPowerManager.wakeUp(wakeTime, reason);
         return true;
     }
 
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index d21c6d2..c5ad7fe 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -472,6 +472,26 @@
     }
 
     /**
+     * Called when the screen has turned on.
+     */
+    public void onWakeUp(String reason, int reasonUid, String opPackageName, int opUid) {
+        if (DEBUG) {
+            Slog.d(TAG, "onWakeUp: event=" + reason + ", reasonUid=" + reasonUid
+                    + " opPackageName=" + opPackageName + " opUid=" + opUid);
+        }
+
+        try {
+            mBatteryStats.noteWakeUp(reason, reasonUid);
+            if (opPackageName != null) {
+                mAppOps.noteOperation(AppOpsManager.OP_TURN_SCREEN_ON, opUid, opPackageName);
+            }
+        } catch (RemoteException ex) {
+            // Ignore
+        }
+
+    }
+
+    /**
      * Called when wireless charging has started so as to provide user feedback.
      */
     public void onWirelessChargingStarted() {
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 3f59755..88476ce 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -830,7 +830,18 @@
     private void applyWakeLockFlagsOnAcquireLocked(WakeLock wakeLock, int uid) {
         if ((wakeLock.mFlags & PowerManager.ACQUIRE_CAUSES_WAKEUP) != 0
                 && isScreenLock(wakeLock)) {
-            wakeUpNoUpdateLocked(SystemClock.uptimeMillis(), uid);
+            String opPackageName;
+            int opUid;
+            if (wakeLock.mWorkSource != null && wakeLock.mWorkSource.getName(0) != null) {
+                opPackageName = wakeLock.mWorkSource.getName(0);
+                opUid = wakeLock.mWorkSource.get(0);
+            } else {
+                opPackageName = wakeLock.mPackageName;
+                opUid = wakeLock.mWorkSource != null ? wakeLock.mWorkSource.get(0)
+                        : wakeLock.mOwnerUid;
+            }
+            wakeUpNoUpdateLocked(SystemClock.uptimeMillis(), wakeLock.mTag, opUid,
+                    opPackageName, opUid);
         }
     }
 
@@ -1042,17 +1053,19 @@
         return false;
     }
 
-    private void wakeUpInternal(long eventTime, int uid) {
+    private void wakeUpInternal(long eventTime, String reason, int uid, String opPackageName,
+            int opUid) {
         synchronized (mLock) {
-            if (wakeUpNoUpdateLocked(eventTime, uid)) {
+            if (wakeUpNoUpdateLocked(eventTime, reason, uid, opPackageName, opUid)) {
                 updatePowerStateLocked();
             }
         }
     }
 
-    private boolean wakeUpNoUpdateLocked(long eventTime, int uid) {
+    private boolean wakeUpNoUpdateLocked(long eventTime, String reason, int reasonUid,
+            String opPackageName, int opUid) {
         if (DEBUG_SPEW) {
-            Slog.d(TAG, "wakeUpNoUpdateLocked: eventTime=" + eventTime + ", uid=" + uid);
+            Slog.d(TAG, "wakeUpNoUpdateLocked: eventTime=" + eventTime + ", uid=" + reasonUid);
         }
 
         if (eventTime < mLastSleepTime || mWakefulness == WAKEFULNESS_AWAKE
@@ -1064,21 +1077,22 @@
         try {
             switch (mWakefulness) {
                 case WAKEFULNESS_ASLEEP:
-                    Slog.i(TAG, "Waking up from sleep (uid " + uid +")...");
+                    Slog.i(TAG, "Waking up from sleep (uid " + reasonUid +")...");
                     break;
                 case WAKEFULNESS_DREAMING:
-                    Slog.i(TAG, "Waking up from dream (uid " + uid +")...");
+                    Slog.i(TAG, "Waking up from dream (uid " + reasonUid +")...");
                     break;
                 case WAKEFULNESS_DOZING:
-                    Slog.i(TAG, "Waking up from dozing (uid " + uid +")...");
+                    Slog.i(TAG, "Waking up from dozing (uid " + reasonUid +")...");
                     break;
             }
 
             mLastWakeTime = eventTime;
             setWakefulnessLocked(WAKEFULNESS_AWAKE, 0);
 
+            mNotifier.onWakeUp(reason, reasonUid, opPackageName, opUid);
             userActivityNoUpdateLocked(
-                    eventTime, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, uid);
+                    eventTime, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, reasonUid);
         } finally {
             Trace.traceEnd(Trace.TRACE_TAG_POWER);
         }
@@ -1334,7 +1348,8 @@
                 final long now = SystemClock.uptimeMillis();
                 if (shouldWakeUpWhenPluggedOrUnpluggedLocked(wasPowered, oldPlugType,
                         dockedOnWirelessCharger)) {
-                    wakeUpNoUpdateLocked(now, Process.SYSTEM_UID);
+                    wakeUpNoUpdateLocked(now, "android.server.power:POWER", Process.SYSTEM_UID,
+                            mContext.getOpPackageName(), Process.SYSTEM_UID);
                 }
                 userActivityNoUpdateLocked(
                         now, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID);
@@ -1788,7 +1803,8 @@
                             PowerManager.GO_TO_SLEEP_REASON_TIMEOUT, 0, Process.SYSTEM_UID);
                     updatePowerStateLocked();
                 } else {
-                    wakeUpNoUpdateLocked(SystemClock.uptimeMillis(), Process.SYSTEM_UID);
+                    wakeUpNoUpdateLocked(SystemClock.uptimeMillis(), "android.server.power:DREAM",
+                            Process.SYSTEM_UID, mContext.getOpPackageName(), Process.SYSTEM_UID);
                     updatePowerStateLocked();
                 }
             } else if (wakefulness == WAKEFULNESS_DOZING) {
@@ -3136,7 +3152,7 @@
         }
 
         @Override // Binder call
-        public void wakeUp(long eventTime) {
+        public void wakeUp(long eventTime, String reason, String opPackageName) {
             if (eventTime > SystemClock.uptimeMillis()) {
                 throw new IllegalArgumentException("event time must not be in the future");
             }
@@ -3147,7 +3163,7 @@
             final int uid = Binder.getCallingUid();
             final long ident = Binder.clearCallingIdentity();
             try {
-                wakeUpInternal(eventTime, uid);
+                wakeUpInternal(eventTime, reason, uid, opPackageName, uid);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index ec566bc..05c111c 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -10307,7 +10307,7 @@
                 if (DEBUG_VISIBILITY || DEBUG_POWER) {
                     Slog.v(TAG, "Turning screen on after layout!");
                 }
-                mPowerManager.wakeUp(SystemClock.uptimeMillis());
+                mPowerManager.wakeUp(SystemClock.uptimeMillis(), "android.server.wm:TURN_ON");
             }
             mTurnOnScreen = false;
         }
diff --git a/services/usb/java/com/android/server/usb/UsbAlsaManager.java b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
index 638783d..31763e7 100644
--- a/services/usb/java/com/android/server/usb/UsbAlsaManager.java
+++ b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
@@ -35,6 +35,7 @@
 
 import com.android.internal.alsa.AlsaCardsParser;
 import com.android.internal.alsa.AlsaDevicesParser;
+import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.audio.AudioService;
 
 import libcore.io.IoUtils;
@@ -502,14 +503,14 @@
     //
     // Logging
     //
-    public void dump(FileDescriptor fd, PrintWriter pw) {
-        pw.println("  USB Audio Devices:");
+    public void dump(IndentingPrintWriter pw) {
+        pw.println("USB Audio Devices:");
         for (UsbDevice device : mAudioDevices.keySet()) {
-            pw.println("    " + device.getDeviceName() + ": " + mAudioDevices.get(device));
+            pw.println("  " + device.getDeviceName() + ": " + mAudioDevices.get(device));
         }
-        pw.println("  USB MIDI Devices:");
+        pw.println("USB MIDI Devices:");
         for (UsbDevice device : mMidiDevices.keySet()) {
-            pw.println("    " + device.getDeviceName() + ": " + mMidiDevices.get(device));
+            pw.println("  " + device.getDeviceName() + ": " + mMidiDevices.get(device));
         }
     }
 
diff --git a/services/usb/java/com/android/server/usb/UsbDebuggingManager.java b/services/usb/java/com/android/server/usb/UsbDebuggingManager.java
index 9a04e8b..ae17fde3 100644
--- a/services/usb/java/com/android/server/usb/UsbDebuggingManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDebuggingManager.java
@@ -38,6 +38,7 @@
 import android.util.Slog;
 
 import com.android.internal.R;
+import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.FgThread;
 
 import java.io.File;
@@ -451,17 +452,17 @@
         mHandler.sendEmptyMessage(UsbDebuggingHandler.MESSAGE_ADB_CLEAR);
     }
 
-    public void dump(FileDescriptor fd, PrintWriter pw) {
-        pw.println("  USB Debugging State:");
-        pw.println("    Connected to adbd: " + (mThread != null));
-        pw.println("    Last key received: " + mFingerprints);
-        pw.println("    User keys:");
+    public void dump(IndentingPrintWriter pw) {
+        pw.println("USB Debugging State:");
+        pw.println("  Connected to adbd: " + (mThread != null));
+        pw.println("  Last key received: " + mFingerprints);
+        pw.println("  User keys:");
         try {
             pw.println(FileUtils.readTextFile(new File("/data/misc/adb/adb_keys"), 0, null));
         } catch (IOException e) {
             pw.println("IOException: " + e);
         }
-        pw.println("    System keys:");
+        pw.println("  System keys:");
         try {
             pw.println(FileUtils.readTextFile(new File("/adb_keys"), 0, null));
         } catch (IOException e) {
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 81b4857..653cbd8 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -45,6 +45,7 @@
 import android.util.Slog;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.FgThread;
 
 import java.io.File;
@@ -807,17 +808,17 @@
                     UsbManager.USB_FUNCTION_ADB);
         }
 
-        public void dump(FileDescriptor fd, PrintWriter pw) {
-            pw.println("  USB Device State:");
-            pw.println("    mCurrentFunctions: " + mCurrentFunctions);
-            pw.println("    mCurrentFunctionsApplied: " + mCurrentFunctionsApplied);
-            pw.println("    mConnected: " + mConnected);
-            pw.println("    mConfigured: " + mConfigured);
-            pw.println("    mCurrentAccessory: " + mCurrentAccessory);
+        public void dump(IndentingPrintWriter pw) {
+            pw.println("USB Device State:");
+            pw.println("  mCurrentFunctions: " + mCurrentFunctions);
+            pw.println("  mCurrentFunctionsApplied: " + mCurrentFunctionsApplied);
+            pw.println("  mConnected: " + mConnected);
+            pw.println("  mConfigured: " + mConfigured);
+            pw.println("  mCurrentAccessory: " + mCurrentAccessory);
             try {
-                pw.println("    Kernel state: "
+                pw.println("  Kernel state: "
                         + FileUtils.readTextFile(new File(STATE_PATH), 0, null).trim());
-                pw.println("    Kernel function list: "
+                pw.println("  Kernel function list: "
                         + FileUtils.readTextFile(new File(FUNCTIONS_PATH), 0, null).trim());
             } catch (IOException e) {
                 pw.println("IOException: " + e);
@@ -908,12 +909,12 @@
         }
     }
 
-    public void dump(FileDescriptor fd, PrintWriter pw) {
+    public void dump(IndentingPrintWriter pw) {
         if (mHandler != null) {
-            mHandler.dump(fd, pw);
+            mHandler.dump(pw);
         }
         if (mDebuggingManager != null) {
-            mDebuggingManager.dump(fd, pw);
+            mDebuggingManager.dump(pw);
         }
     }
 
diff --git a/services/usb/java/com/android/server/usb/UsbHostManager.java b/services/usb/java/com/android/server/usb/UsbHostManager.java
index f5f2b07..6300a9a 100644
--- a/services/usb/java/com/android/server/usb/UsbHostManager.java
+++ b/services/usb/java/com/android/server/usb/UsbHostManager.java
@@ -27,6 +27,7 @@
 import android.util.Slog;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.IndentingPrintWriter;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -283,11 +284,11 @@
         }
     }
 
-    public void dump(FileDescriptor fd, PrintWriter pw) {
+    public void dump(IndentingPrintWriter pw) {
         synchronized (mLock) {
-            pw.println("  USB Host State:");
+            pw.println("USB Host State:");
             for (String name : mDevices.keySet()) {
-                pw.println("    " + name + ": " + mDevices.get(name));
+                pw.println("  " + name + ": " + mDevices.get(name));
             }
         }
     }
diff --git a/services/usb/java/com/android/server/usb/UsbPortManager.java b/services/usb/java/com/android/server/usb/UsbPortManager.java
new file mode 100644
index 0000000..52abcfe
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/UsbPortManager.java
@@ -0,0 +1,753 @@
+/*
+ * Copyright (C) 2015 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;
+
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.FgThread;
+
+import android.content.Context;
+import android.content.Intent;
+import android.hardware.usb.UsbManager;
+import android.hardware.usb.UsbPort;
+import android.hardware.usb.UsbPortStatus;
+import android.os.Handler;
+import android.os.Message;
+import android.os.UEventObserver;
+import android.os.UserHandle;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.util.Slog;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+
+import libcore.io.IoUtils;
+
+/**
+ * Allows trusted components to control the properties of physical USB ports
+ * via the "/sys/class/dual_role_usb" kernel interface.
+ * <p>
+ * Note: This interface may not be supported on all chipsets since the USB drivers
+ * must be changed to publish this information through the module.  At the moment
+ * we only need this for devices with USB Type C ports to allow the System UI to
+ * control USB charging and data direction.  On devices that do not support this
+ * interface the list of ports may incorrectly appear to be empty
+ * (but we don't care today).
+ * </p>
+ */
+public class UsbPortManager {
+    private static final String TAG = "UsbPortManager";
+
+    private static final int MSG_UPDATE_PORTS = 1;
+
+    // UEvent path to watch.
+    private static final String UEVENT_FILTER = "SUBSYSTEM=dual_role_usb";
+
+    // SysFS directory that contains USB ports as subdirectories.
+    private static final String SYSFS_CLASS = "/sys/class/dual_role_usb";
+
+    // SysFS file that contains a USB port's supported modes.  (read-only)
+    // Contents: "", "ufp", "dfp", or "ufp dfp".
+    private static final String SYSFS_PORT_SUPPORTED_MODES = "supported_modes";
+
+    // SysFS file that contains a USB port's current mode.  (read-write if configurable)
+    // Contents: "", "ufp", or "dfp".
+    private static final String SYSFS_PORT_MODE = "mode";
+
+    // SysFS file that contains a USB port's current power role.  (read-write if configurable)
+    // Contents: "", "source", or "sink".
+    private static final String SYSFS_PORT_POWER_ROLE = "power_role";
+
+    // SysFS file that contains a USB port's current data role.  (read-write if configurable)
+    // Contents: "", "host", or "device".
+    private static final String SYSFS_PORT_DATA_ROLE = "data_role";
+
+    // Port modes: upstream facing port or downstream facing port.
+    private static final String PORT_MODE_DFP = "dfp";
+    private static final String PORT_MODE_UFP = "ufp";
+
+    // Port power roles: source or sink.
+    private static final String PORT_POWER_ROLE_SOURCE = "source";
+    private static final String PORT_POWER_ROLE_SINK = "sink";
+
+    // Port data roles: host or device.
+    private static final String PORT_DATA_ROLE_HOST = "host";
+    private static final String PORT_DATA_ROLE_DEVICE = "device";
+
+    // All non-trivial role combinations.
+    private static final int COMBO_SOURCE_HOST =
+            UsbPort.combineRolesAsBit(UsbPort.POWER_ROLE_SOURCE, UsbPort.DATA_ROLE_HOST);
+    private static final int COMBO_SOURCE_DEVICE =
+            UsbPort.combineRolesAsBit(UsbPort.POWER_ROLE_SOURCE, UsbPort.DATA_ROLE_DEVICE);
+    private static final int COMBO_SINK_HOST =
+            UsbPort.combineRolesAsBit(UsbPort.POWER_ROLE_SINK, UsbPort.DATA_ROLE_HOST);
+    private static final int COMBO_SINK_DEVICE =
+            UsbPort.combineRolesAsBit(UsbPort.POWER_ROLE_SINK, UsbPort.DATA_ROLE_DEVICE);
+
+    // The system context.
+    private final Context mContext;
+
+    // True if we have kernel support.
+    private final boolean mHaveKernelSupport;
+
+    // Mutex for all mutable shared state.
+    private final Object mLock = new Object();
+
+    // List of all ports, indexed by id.
+    // Ports may temporarily have different dispositions as they are added or removed
+    // but the class invariant is that this list will only contain ports with DISPOSITION_READY
+    // except while updatePortsLocked() is in progress.
+    private final ArrayMap<String, PortInfo> mPorts = new ArrayMap<String, PortInfo>();
+
+    // List of all simulated ports, indexed by id.
+    private final ArrayMap<String, SimulatedPortInfo> mSimulatedPorts =
+            new ArrayMap<String, SimulatedPortInfo>();
+
+    public UsbPortManager(Context context) {
+        mContext = context;
+        mHaveKernelSupport = new File(SYSFS_CLASS).exists();
+    }
+
+    public void systemReady() {
+        mUEventObserver.startObserving(UEVENT_FILTER);
+        scheduleUpdatePorts();
+    }
+
+    public UsbPort[] getPorts() {
+        synchronized (mLock) {
+            final int count = mPorts.size();
+            final UsbPort[] result = new UsbPort[count];
+            for (int i = 0; i < count; i++) {
+                result[i] = mPorts.valueAt(i).mUsbPort;
+            }
+            return result;
+        }
+    }
+
+    public UsbPortStatus getPortStatus(String portId) {
+        synchronized (mLock) {
+            final PortInfo portInfo = mPorts.get(portId);
+            return portInfo != null ? portInfo.mUsbPortStatus : null;
+        }
+    }
+
+    public void setPortRoles(String portId, int newPowerRole, int newDataRole,
+            IndentingPrintWriter pw) {
+        synchronized (mLock) {
+            final PortInfo portInfo = mPorts.get(portId);
+            if (portInfo == null) {
+                if (pw != null) {
+                    pw.println("No such USB port: " + portId);
+                }
+                return;
+            }
+
+            // Check whether the new role is actually supported.
+            if (!portInfo.mUsbPortStatus.isRoleCombinationSupported(newPowerRole, newDataRole)) {
+                logAndPrint(Log.ERROR, pw, "Attempted to set USB port into unsupported "
+                        + "role combination: portId=" + portId
+                        + ", newPowerRole=" + UsbPort.powerRoleToString(newPowerRole)
+                        + ", newDataRole=" + UsbPort.dataRoleToString(newDataRole));
+                return;
+            }
+
+            // Check whether anything actually changed.
+            final int currentDataRole = portInfo.mUsbPortStatus.getCurrentDataRole();
+            final int currentPowerRole = portInfo.mUsbPortStatus.getCurrentPowerRole();
+            if (currentDataRole == newDataRole && currentPowerRole == newPowerRole) {
+                if (pw != null) {
+                    pw.println("No change.");
+                }
+                return;
+            }
+
+            // Determine whether we need to change the mode in order to accomplish this goal.
+            // We prefer not to do this since it's more likely to fail.
+            //
+            // Note: Arguably it might be worth allowing the client to influence this policy
+            // decision so that we could show more powerful developer facing UI but let's
+            // see how far we can get without having to do that.
+            final boolean canChangeMode = portInfo.mCanChangeMode;
+            final boolean canChangePowerRole = portInfo.mCanChangePowerRole;
+            final boolean canChangeDataRole = portInfo.mCanChangeDataRole;
+            final int currentMode = portInfo.mUsbPortStatus.getCurrentMode();
+            final int newMode;
+            if ((!canChangePowerRole && currentPowerRole != newPowerRole)
+                    || (!canChangeDataRole && currentDataRole != newDataRole)) {
+                if (canChangeMode && newPowerRole == UsbPort.POWER_ROLE_SOURCE
+                        && newDataRole == UsbPort.DATA_ROLE_HOST) {
+                    newMode = UsbPort.MODE_DFP;
+                } else if (canChangeMode && newPowerRole == UsbPort.POWER_ROLE_SINK
+                        && newDataRole == UsbPort.DATA_ROLE_DEVICE) {
+                    newMode = UsbPort.MODE_UFP;
+                } else {
+                    logAndPrint(Log.ERROR, pw, "Found mismatch in supported USB role combinations "
+                            + "while attempting to change role: " + portInfo
+                            + ", newPowerRole=" + UsbPort.powerRoleToString(newPowerRole)
+                            + ", newDataRole=" + UsbPort.dataRoleToString(newDataRole));
+                    return;
+                }
+            } else {
+                newMode = currentMode;
+            }
+
+            // Make it happen.
+            logAndPrint(Log.INFO, pw, "Setting USB port mode and role: portId=" + portId
+                    + ", currentMode=" + UsbPort.modeToString(currentMode)
+                    + ", currentPowerRole=" + UsbPort.powerRoleToString(currentPowerRole)
+                    + ", currentDataRole=" + UsbPort.dataRoleToString(currentDataRole)
+                    + ", newMode=" + UsbPort.modeToString(newMode)
+                    + ", newPowerRole=" + UsbPort.powerRoleToString(newPowerRole)
+                    + ", newDataRole=" + UsbPort.dataRoleToString(newDataRole));
+
+            SimulatedPortInfo sim = mSimulatedPorts.get(portId);
+            if (sim != null) {
+                // Change simulated state.
+                sim.mCurrentMode = newMode;
+                sim.mCurrentPowerRole = newPowerRole;
+                sim.mCurrentDataRole = newDataRole;
+            } else if (mHaveKernelSupport) {
+                // Change actual state.
+                final File portDir = new File(SYSFS_CLASS, portId);
+                if (!portDir.exists()) {
+                    logAndPrint(Log.ERROR, pw, "USB port not found: portId=" + portId);
+                    return;
+                }
+
+                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
+                    // and the renegotiation might fail.  Due to limitations of the USB
+                    // hardware, we have no way of knowing whether it will work apriori
+                    // which is why we would prefer to set the power and data roles
+                    // directly instead.
+                    if (!writeFile(portDir, SYSFS_PORT_MODE,
+                            newMode == UsbPort.MODE_DFP ? PORT_MODE_DFP : PORT_MODE_UFP)) {
+                        logAndPrint(Log.ERROR, pw, "Failed to set the USB port mode: "
+                                + "portId=" + portId
+                                + ", newMode=" + UsbPort.modeToString(newMode));
+                        return;
+                    }
+                } else {
+                    // Change power and data role independently as needed.
+                    if (currentPowerRole != newPowerRole) {
+                        if (!writeFile(portDir, SYSFS_PORT_POWER_ROLE,
+                                newPowerRole == UsbPort.POWER_ROLE_SOURCE
+                                ? PORT_POWER_ROLE_SOURCE : PORT_POWER_ROLE_SINK)) {
+                            logAndPrint(Log.ERROR, pw, "Failed to set the USB port power role: "
+                                    + "portId=" + portId
+                                    + ", newPowerRole=" + UsbPort.powerRoleToString(newPowerRole));
+                            return;
+                        }
+                    }
+                    if (currentDataRole != newDataRole) {
+                        if (!writeFile(portDir, SYSFS_PORT_DATA_ROLE,
+                                newDataRole == UsbPort.DATA_ROLE_HOST
+                                ? PORT_DATA_ROLE_HOST : PORT_DATA_ROLE_DEVICE)) {
+                            logAndPrint(Log.ERROR, pw, "Failed to set the USB port data role: "
+                                    + "portId=" + portId
+                                    + ", newDataRole=" + UsbPort.dataRoleToString(newDataRole));
+                            return;
+                        }
+                    }
+                }
+            }
+            updatePortsLocked(pw);
+        }
+    }
+
+    public void addSimulatedPort(String portId, int supportedModes, IndentingPrintWriter pw) {
+        synchronized (mLock) {
+            if (mSimulatedPorts.containsKey(portId)) {
+                pw.println("Port with same name already exists.  Please remove it first.");
+                return;
+            }
+
+            pw.println("Adding simulated port: portId=" + portId
+                    + ", supportedModes=" + UsbPort.modeToString(supportedModes));
+            mSimulatedPorts.put(portId,
+                    new SimulatedPortInfo(portId, supportedModes));
+            updatePortsLocked(pw);
+        }
+    }
+
+    public void connectSimulatedPort(String portId, int mode, boolean canChangeMode,
+            int powerRole, boolean canChangePowerRole,
+            int dataRole, boolean canChangeDataRole, IndentingPrintWriter pw) {
+        synchronized (mLock) {
+            final SimulatedPortInfo portInfo = mSimulatedPorts.get(portId);
+            if (portInfo == null) {
+                pw.println("Cannot connect simulated port which does not exist.");
+                return;
+            }
+
+            if (mode == 0 || powerRole == 0 || dataRole == 0) {
+                pw.println("Cannot connect simulated port in null mode, "
+                        + "power role, or data role.");
+                return;
+            }
+
+            if ((portInfo.mSupportedModes & mode) == 0) {
+                pw.println("Simulated port does not support mode: " + UsbPort.modeToString(mode));
+                return;
+            }
+
+            pw.println("Connecting simulated port: portId=" + portId
+                    + ", mode=" + UsbPort.modeToString(mode)
+                    + ", canChangeMode=" + canChangeMode
+                    + ", powerRole=" + UsbPort.powerRoleToString(powerRole)
+                    + ", canChangePowerRole=" + canChangePowerRole
+                    + ", dataRole=" + UsbPort.dataRoleToString(dataRole)
+                    + ", canChangeDataRole=" + canChangeDataRole);
+            portInfo.mCurrentMode = mode;
+            portInfo.mCanChangeMode = canChangeMode;
+            portInfo.mCurrentPowerRole = powerRole;
+            portInfo.mCanChangePowerRole = canChangePowerRole;
+            portInfo.mCurrentDataRole = dataRole;
+            portInfo.mCanChangeDataRole = canChangeDataRole;
+            updatePortsLocked(pw);
+        }
+    }
+
+    public void disconnectSimulatedPort(String portId, IndentingPrintWriter pw) {
+        synchronized (mLock) {
+            final SimulatedPortInfo portInfo = mSimulatedPorts.get(portId);
+            if (portInfo == null) {
+                pw.println("Cannot disconnect simulated port which does not exist.");
+                return;
+            }
+
+            pw.println("Disconnecting simulated port: portId=" + portId);
+            portInfo.mCurrentMode = 0;
+            portInfo.mCanChangeMode = false;
+            portInfo.mCurrentPowerRole = 0;
+            portInfo.mCanChangePowerRole = false;
+            portInfo.mCurrentDataRole = 0;
+            portInfo.mCanChangeDataRole = false;
+            updatePortsLocked(pw);
+        }
+    }
+
+    public void removeSimulatedPort(String portId, IndentingPrintWriter pw) {
+        synchronized (mLock) {
+            final int index = mSimulatedPorts.indexOfKey(portId);
+            if (index < 0) {
+                pw.println("Cannot remove simulated port which does not exist.");
+                return;
+            }
+
+            pw.println("Disconnecting simulated port: portId=" + portId);
+            mSimulatedPorts.removeAt(index);
+            updatePortsLocked(pw);
+        }
+    }
+
+    public void resetSimulation(IndentingPrintWriter pw) {
+        synchronized (mLock) {
+            pw.println("Removing all simulated ports and ending simulation.");
+            if (!mSimulatedPorts.isEmpty()) {
+                mSimulatedPorts.clear();
+                updatePortsLocked(pw);
+            }
+        }
+    }
+
+    public void dump(IndentingPrintWriter pw) {
+        synchronized (mLock) {
+            pw.print("USB Port State:");
+            if (!mSimulatedPorts.isEmpty()) {
+                pw.print(" (simulation active; end with 'dumpsys usb reset')");
+            }
+            pw.println();
+
+            if (mPorts.isEmpty()) {
+                pw.println("  <no ports>");
+            } else {
+                for (PortInfo portInfo : mPorts.values()) {
+                    pw.println("  " + portInfo.mUsbPort.getId() + ": " + portInfo);
+                }
+            }
+        }
+    }
+
+    private void updatePortsLocked(IndentingPrintWriter pw) {
+        // Assume all ports are gone unless informed otherwise.
+        // Kind of pessimistic but simple.
+        for (int i = mPorts.size(); i-- > 0; ) {
+            mPorts.valueAt(i).mDisposition = PortInfo.DISPOSITION_REMOVED;
+        }
+
+        // Enumerate all extant ports.
+        if (!mSimulatedPorts.isEmpty()) {
+            final int count = mSimulatedPorts.size();
+            for (int i = 0; i < count; i++) {
+                final SimulatedPortInfo portInfo = mSimulatedPorts.valueAt(i);
+                addOrUpdatePortLocked(portInfo.mPortId, portInfo.mSupportedModes,
+                        portInfo.mCurrentMode, portInfo.mCanChangeMode,
+                        portInfo.mCurrentPowerRole, portInfo.mCanChangePowerRole,
+                        portInfo.mCurrentDataRole, portInfo.mCanChangeDataRole, pw);
+            }
+        } else if (mHaveKernelSupport) {
+            final File[] portDirs = new File(SYSFS_CLASS).listFiles();
+            if (portDirs != null) {
+                for (File portDir : portDirs) {
+                    if (!portDir.isDirectory()) {
+                        continue;
+                    }
+
+                    // Parse the sysfs file contents.
+                    final String portId = portDir.getName();
+                    final int supportedModes = readSupportedModes(portDir);
+                    final int currentMode = readCurrentMode(portDir);
+                    final boolean canChangeMode = canChangeMode(portDir);
+                    final int currentPowerRole = readCurrentPowerRole(portDir);
+                    final boolean canChangePowerRole = canChangePowerRole(portDir);
+                    final int currentDataRole = readCurrentDataRole(portDir);
+                    final boolean canChangeDataRole = canChangeDataRole(portDir);
+                    addOrUpdatePortLocked(portId, supportedModes,
+                            currentMode, canChangeMode,
+                            currentPowerRole, canChangePowerRole,
+                            currentDataRole, canChangeDataRole, pw);
+                 }
+            }
+        }
+
+        // Process the updates.
+        // Once finished, the list of ports will only contain ports in DISPOSITION_READY.
+        for (int i = mPorts.size(); i-- > 0; ) {
+            final PortInfo portInfo = mPorts.valueAt(i);
+            switch (portInfo.mDisposition) {
+                case PortInfo.DISPOSITION_ADDED:
+                    handlePortAddedLocked(portInfo, pw);
+                    portInfo.mDisposition = PortInfo.DISPOSITION_READY;
+                    break;
+                case PortInfo.DISPOSITION_CHANGED:
+                    handlePortChangedLocked(portInfo, pw);
+                    portInfo.mDisposition = PortInfo.DISPOSITION_READY;
+                    break;
+                case PortInfo.DISPOSITION_REMOVED:
+                    mPorts.removeAt(i);
+                    portInfo.mUsbPortStatus = null; // must do this early
+                    handlePortRemovedLocked(portInfo, pw);
+                    break;
+            }
+        }
+    }
+
+    // Must only be called by updatePortsLocked.
+    private void addOrUpdatePortLocked(String portId, int supportedModes,
+            int currentMode, boolean canChangeMode,
+            int currentPowerRole, boolean canChangePowerRole,
+            int currentDataRole, boolean canChangeDataRole,
+            IndentingPrintWriter pw) {
+        // Only allow mode switch capability for dual role ports.
+        // Validate that the current mode matches the supported modes we expect.
+        if (supportedModes != UsbPort.MODE_DUAL) {
+            canChangeMode = false;
+            if (currentMode != 0 && currentMode != supportedModes) {
+                logAndPrint(Log.WARN, pw, "Ignoring inconsistent current mode from USB "
+                        + "port driver: supportedModes=" + UsbPort.modeToString(supportedModes)
+                        + ", currentMode=" + UsbPort.modeToString(currentMode));
+                currentMode = 0;
+            }
+        }
+
+        // Determine the supported role combinations.
+        // Note that the policy is designed to prefer setting the power and data
+        // role independently rather than changing the mode.
+        int supportedRoleCombinations = UsbPort.combineRolesAsBit(
+                currentPowerRole, currentDataRole);
+        if (currentMode != 0 && currentPowerRole != 0 && currentDataRole != 0) {
+            if (canChangePowerRole && canChangeDataRole) {
+                // Can change both power and data role independently.
+                // Assume all combinations are possible.
+                supportedRoleCombinations |=
+                        COMBO_SOURCE_HOST | COMBO_SOURCE_DEVICE
+                                | COMBO_SINK_HOST | COMBO_SINK_DEVICE;
+            } else if (canChangePowerRole) {
+                // Can only change power role.
+                // Assume data role must remain at its current value.
+                supportedRoleCombinations |= UsbPort.combineRolesAsBit(
+                        UsbPort.POWER_ROLE_SOURCE, currentDataRole);
+                supportedRoleCombinations |= UsbPort.combineRolesAsBit(
+                        UsbPort.POWER_ROLE_SINK, currentDataRole);
+            } else if (canChangeDataRole) {
+                // Can only change data role.
+                // Assume power role must remain at its current value.
+                supportedRoleCombinations |= UsbPort.combineRolesAsBit(
+                        currentPowerRole, UsbPort.DATA_ROLE_HOST);
+                supportedRoleCombinations |= UsbPort.combineRolesAsBit(
+                        currentPowerRole, UsbPort.DATA_ROLE_DEVICE);
+            } else if (canChangeMode) {
+                // Can only change the mode.
+                // Assume both standard UFP and DFP configurations will become available
+                // when this happens.
+                supportedRoleCombinations |= COMBO_SOURCE_HOST | COMBO_SINK_DEVICE;
+            }
+        }
+
+        // Update the port data structures.
+        PortInfo portInfo = mPorts.get(portId);
+        if (portInfo == null) {
+            portInfo = new PortInfo(portId, supportedModes);
+            portInfo.setStatus(currentMode, canChangeMode,
+                    currentPowerRole, canChangePowerRole,
+                    currentDataRole, canChangeDataRole,
+                    supportedRoleCombinations);
+            mPorts.put(portId, portInfo);
+        } else {
+            // Sanity check that ports aren't changing definition out from under us.
+            if (supportedModes != portInfo.mUsbPort.getSupportedModes()) {
+                logAndPrint(Log.WARN, pw, "Ignoring inconsistent list of supported modes from "
+                        + "USB port driver (should be immutable): "
+                        + "previous=" + UsbPort.modeToString(
+                                portInfo.mUsbPort.getSupportedModes())
+                        + ", current=" + UsbPort.modeToString(supportedModes));
+            }
+
+            if (portInfo.setStatus(currentMode, canChangeMode,
+                    currentPowerRole, canChangePowerRole,
+                    currentDataRole, canChangeDataRole,
+                    supportedRoleCombinations)) {
+                portInfo.mDisposition = PortInfo.DISPOSITION_CHANGED;
+            } else {
+                portInfo.mDisposition = PortInfo.DISPOSITION_READY;
+            }
+        }
+    }
+
+    private void handlePortAddedLocked(PortInfo portInfo, IndentingPrintWriter pw) {
+        logAndPrint(Log.INFO, pw, "USB port added: " + portInfo);
+        sendPortChangedBroadcastLocked(portInfo);
+    }
+
+    private void handlePortChangedLocked(PortInfo portInfo, IndentingPrintWriter pw) {
+        logAndPrint(Log.INFO, pw, "USB port changed: " + portInfo);
+        sendPortChangedBroadcastLocked(portInfo);
+    }
+
+    private void handlePortRemovedLocked(PortInfo portInfo, IndentingPrintWriter pw) {
+        logAndPrint(Log.INFO, pw, "USB port removed: " + portInfo);
+        sendPortChangedBroadcastLocked(portInfo);
+    }
+
+    private void sendPortChangedBroadcastLocked(PortInfo portInfo) {
+        final Intent intent = new Intent(UsbManager.ACTION_USB_PORT_CHANGED);
+        intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+        intent.putExtra(UsbManager.EXTRA_PORT, portInfo.mUsbPort);
+        intent.putExtra(UsbManager.EXTRA_PORT_STATUS, portInfo.mUsbPortStatus);
+
+        // Guard against possible reentrance by posting the broadcast from the handler
+        // instead of from within the critical section.
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+            }
+        });
+    }
+
+    private void scheduleUpdatePorts() {
+        if (!mHandler.hasMessages(MSG_UPDATE_PORTS)) {
+            mHandler.sendEmptyMessage(MSG_UPDATE_PORTS);
+        }
+    }
+
+    private static int readSupportedModes(File portDir) {
+        int modes = 0;
+        final String contents = readFile(portDir, SYSFS_PORT_SUPPORTED_MODES);
+        if (contents != null) {
+            if (contents.contains(PORT_MODE_DFP)) {
+                modes |= UsbPort.MODE_DFP;
+            }
+            if (contents.contains(PORT_MODE_UFP)) {
+                modes |= UsbPort.MODE_UFP;
+            }
+        }
+        return modes;
+    }
+
+    private static int readCurrentMode(File portDir) {
+        final String contents = readFile(portDir, SYSFS_PORT_MODE);
+        if (contents != null) {
+            if (contents.equals(PORT_MODE_DFP)) {
+                return UsbPort.MODE_DFP;
+            }
+            if (contents.equals(PORT_MODE_UFP)) {
+                return UsbPort.MODE_UFP;
+            }
+        }
+        return 0;
+    }
+
+    private static int readCurrentPowerRole(File portDir) {
+        final String contents = readFile(portDir, SYSFS_PORT_POWER_ROLE);
+        if (contents != null) {
+            if (contents.equals(PORT_POWER_ROLE_SOURCE)) {
+                return UsbPort.POWER_ROLE_SOURCE;
+            }
+            if (contents.equals(PORT_POWER_ROLE_SINK)) {
+                return UsbPort.POWER_ROLE_SINK;
+            }
+        }
+        return 0;
+    }
+
+    private static int readCurrentDataRole(File portDir) {
+        final String contents = readFile(portDir, SYSFS_PORT_DATA_ROLE);
+        if (contents != null) {
+            if (contents.equals(PORT_DATA_ROLE_HOST)) {
+                return UsbPort.DATA_ROLE_HOST;
+            }
+            if (contents.equals(PORT_DATA_ROLE_DEVICE)) {
+                return UsbPort.DATA_ROLE_DEVICE;
+            }
+        }
+        return 0;
+    }
+
+    private static boolean canChangeMode(File portDir) {
+        return new File(portDir, SYSFS_PORT_MODE).canWrite();
+    }
+
+    private static boolean canChangePowerRole(File portDir) {
+        return new File(portDir, SYSFS_PORT_POWER_ROLE).canWrite();
+    }
+
+    private static boolean canChangeDataRole(File portDir) {
+        return new File(portDir, SYSFS_PORT_DATA_ROLE).canWrite();
+    }
+
+    private static String readFile(File dir, String filename) {
+        final File file = new File(dir, filename);
+        try {
+            return IoUtils.readFileAsString(file.getAbsolutePath()).trim();
+        } catch (IOException ex) {
+            return null;
+        }
+    }
+
+    private static boolean writeFile(File dir, String filename, String contents) {
+        final File file = new File(dir, filename);
+        try {
+            try (FileWriter writer = new FileWriter(file)) {
+                writer.write(contents);
+            }
+            return true;
+        } catch (IOException ex) {
+            return false;
+        }
+    }
+
+    private static void logAndPrint(int priority, IndentingPrintWriter pw, String msg) {
+        Slog.println(priority, TAG, msg);
+        if (pw != null) {
+            pw.println(msg);
+        }
+    }
+
+    private final Handler mHandler = new Handler(FgThread.get().getLooper()) {
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_UPDATE_PORTS: {
+                    synchronized (mLock) {
+                        updatePortsLocked(null);
+                    }
+                    break;
+                }
+            }
+        }
+    };
+
+    private final UEventObserver mUEventObserver = new UEventObserver() {
+        @Override
+        public void onUEvent(UEvent event) {
+            scheduleUpdatePorts();
+        }
+    };
+
+    /**
+     * Describes a USB port.
+     */
+    private 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;
+        public static final int DISPOSITION_REMOVED = 3;
+
+        public final UsbPort mUsbPort;
+        public UsbPortStatus mUsbPortStatus;
+        public boolean mCanChangeMode;
+        public boolean mCanChangePowerRole;
+        public boolean mCanChangeDataRole;
+        public int mDisposition; // default initialized to 0 which means added
+
+        public PortInfo(String portId, int supportedModes) {
+            mUsbPort = new UsbPort(portId, supportedModes);
+        }
+
+        public boolean setStatus(int currentMode, boolean canChangeMode,
+                int currentPowerRole, boolean canChangePowerRole,
+                int currentDataRole, boolean canChangeDataRole,
+                int supportedRoleCombinations) {
+            mCanChangeMode = canChangeMode;
+            mCanChangePowerRole = canChangePowerRole;
+            mCanChangeDataRole = canChangeDataRole;
+            if (mUsbPortStatus == null
+                    || mUsbPortStatus.getCurrentMode() != currentMode
+                    || mUsbPortStatus.getCurrentPowerRole() != currentPowerRole
+                    || mUsbPortStatus.getCurrentDataRole() != currentDataRole
+                    || mUsbPortStatus.getSupportedRoleCombinations()
+                            != supportedRoleCombinations) {
+                mUsbPortStatus = new UsbPortStatus(currentMode, currentPowerRole, currentDataRole,
+                        supportedRoleCombinations);
+                return true;
+            }
+            return false;
+        }
+
+        @Override
+        public String toString() {
+            return "port=" + mUsbPort + ", status=" + mUsbPortStatus
+                    + ", canChangeMode=" + mCanChangeMode
+                    + ", canChangePowerRole=" + mCanChangePowerRole
+                    + ", canChangeDataRole=" + mCanChangeDataRole;
+        }
+    }
+
+    /**
+     * Describes a simulated USB port.
+     * Roughly mirrors the information we would ordinarily get from the kernel.
+     */
+    private static final class SimulatedPortInfo {
+        public final String mPortId;
+        public final int mSupportedModes;
+        public int mCurrentMode;
+        public boolean mCanChangeMode;
+        public int mCurrentPowerRole;
+        public boolean mCanChangePowerRole;
+        public int mCurrentDataRole;
+        public boolean mCanChangeDataRole;
+
+        public SimulatedPortInfo(String portId, int supportedModes) {
+            mPortId = portId;
+            mSupportedModes = supportedModes;
+        }
+    }
+}
diff --git a/services/usb/java/com/android/server/usb/UsbService.java b/services/usb/java/com/android/server/usb/UsbService.java
index c8b42262..f93a2ef 100644
--- a/services/usb/java/com/android/server/usb/UsbService.java
+++ b/services/usb/java/com/android/server/usb/UsbService.java
@@ -27,6 +27,9 @@
 import android.hardware.usb.UsbAccessory;
 import android.hardware.usb.UsbDevice;
 import android.hardware.usb.UsbManager;
+import android.hardware.usb.UsbPort;
+import android.hardware.usb.UsbPortStatus;
+import android.os.Binder;
 import android.os.Bundle;
 import android.os.ParcelFileDescriptor;
 import android.os.UserHandle;
@@ -36,6 +39,7 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.IndentingPrintWriter;
+import com.android.internal.util.Preconditions;
 import com.android.server.SystemService;
 
 import java.io.File;
@@ -78,6 +82,7 @@
 
     private UsbDeviceManager mDeviceManager;
     private UsbHostManager mHostManager;
+    private UsbPortManager mPortManager;
     private final UsbAlsaManager mAlsaManager;
 
     private final Object mLock = new Object();
@@ -110,6 +115,9 @@
         if (new File("/sys/class/android_usb").exists()) {
             mDeviceManager = new UsbDeviceManager(context, mAlsaManager);
         }
+        if (mHostManager != null || mDeviceManager != null) {
+            mPortManager = new UsbPortManager(context);
+        }
 
         setCurrentUser(UserHandle.USER_OWNER);
 
@@ -160,6 +168,9 @@
         if (mHostManager != null) {
             mHostManager.systemReady();
         }
+        if (mPortManager != null) {
+            mPortManager.systemReady();
+        }
     }
 
     public void bootCompleted() {
@@ -346,29 +357,258 @@
     }
 
     @Override
+    public UsbPort[] getPorts() {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
+
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            return mPortManager != null ? mPortManager.getPorts() : null;
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    @Override
+    public UsbPortStatus getPortStatus(String portId) {
+        Preconditions.checkNotNull(portId, "portId must not be null");
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
+
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            return mPortManager != null ? mPortManager.getPortStatus(portId) : null;
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    @Override
+    public void setPortRoles(String portId, int powerRole, int dataRole) {
+        Preconditions.checkNotNull(portId, "portId must not be null");
+        UsbPort.checkRoles(powerRole, dataRole);
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
+
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            if (mPortManager != null) {
+                mPortManager.setPortRoles(portId, powerRole, dataRole, null);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    @Override
     public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
+
         final IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ");
-
-        pw.println("USB Manager State:");
-        if (mDeviceManager != null) {
-            mDeviceManager.dump(fd, pw);
-        }
-        if (mHostManager != null) {
-            mHostManager.dump(fd, pw);
-        }
-        mAlsaManager.dump(fd, pw);
-
-        synchronized (mLock) {
-            for (int i = 0; i < mSettingsByUser.size(); i++) {
-                final int userId = mSettingsByUser.keyAt(i);
-                final UsbSettingsManager settings = mSettingsByUser.valueAt(i);
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            if (args == null || args.length == 0 || "-a".equals(args[0])) {
+                pw.println("USB Manager State:");
                 pw.increaseIndent();
-                pw.println("Settings for user " + userId + ":");
-                settings.dump(fd, pw);
-                pw.decreaseIndent();
+                if (mDeviceManager != null) {
+                    mDeviceManager.dump(pw);
+                }
+                if (mHostManager != null) {
+                    mHostManager.dump(pw);
+                }
+                if (mPortManager != null) {
+                    mPortManager.dump(pw);
+                }
+                mAlsaManager.dump(pw);
+
+                synchronized (mLock) {
+                    for (int i = 0; i < mSettingsByUser.size(); i++) {
+                        final int userId = mSettingsByUser.keyAt(i);
+                        final UsbSettingsManager settings = mSettingsByUser.valueAt(i);
+                        pw.println("Settings for user " + userId + ":");
+                        pw.increaseIndent();
+                        settings.dump(pw);
+                        pw.decreaseIndent();
+                    }
+                }
+            } else if (args.length == 4 && "set-port-roles".equals(args[0])) {
+                final String portId = args[1];
+                final int powerRole;
+                switch (args[2]) {
+                    case "source":
+                        powerRole = UsbPort.POWER_ROLE_SOURCE;
+                        break;
+                    case "sink":
+                        powerRole = UsbPort.POWER_ROLE_SINK;
+                        break;
+                    case "no-power":
+                        powerRole = 0;
+                        break;
+                    default:
+                        pw.println("Invalid power role: " + args[2]);
+                        return;
+                }
+                final int dataRole;
+                switch (args[3]) {
+                    case "host":
+                        dataRole = UsbPort.DATA_ROLE_HOST;
+                        break;
+                    case "device":
+                        dataRole = UsbPort.DATA_ROLE_DEVICE;
+                        break;
+                    case "no-data":
+                        dataRole = 0;
+                        break;
+                    default:
+                        pw.println("Invalid data role: " + args[3]);
+                        return;
+                }
+                if (mPortManager != null) {
+                    mPortManager.setPortRoles(portId, powerRole, dataRole, pw);
+                    // Note: It might take some time for the side-effects of this operation
+                    // to be fully applied by the kernel since the driver may need to
+                    // renegotiate the USB port mode.  If this proves to be an issue
+                    // during debugging, it might be worth adding a sleep here before
+                    // dumping the new state.
+                    pw.println();
+                    mPortManager.dump(pw);
+                }
+            } else if (args.length == 3 && "add-port".equals(args[0])) {
+                final String portId = args[1];
+                final int supportedModes;
+                switch (args[2]) {
+                    case "ufp":
+                        supportedModes = UsbPort.MODE_UFP;
+                        break;
+                    case "dfp":
+                        supportedModes = UsbPort.MODE_DFP;
+                        break;
+                    case "dual":
+                        supportedModes = UsbPort.MODE_DUAL;
+                        break;
+                    case "none":
+                        supportedModes = 0;
+                        break;
+                    default:
+                        pw.println("Invalid mode: " + args[2]);
+                        return;
+                }
+                if (mPortManager != null) {
+                    mPortManager.addSimulatedPort(portId, supportedModes, pw);
+                    pw.println();
+                    mPortManager.dump(pw);
+                }
+            } else if (args.length == 5 && "connect-port".equals(args[0])) {
+                final String portId = args[1];
+                final int mode;
+                final boolean canChangeMode = args[2].endsWith("?");
+                switch (canChangeMode ? removeLastChar(args[2]) : args[2]) {
+                    case "ufp":
+                        mode = UsbPort.MODE_UFP;
+                        break;
+                    case "dfp":
+                        mode = UsbPort.MODE_DFP;
+                        break;
+                    default:
+                        pw.println("Invalid mode: " + args[2]);
+                        return;
+                }
+                final int powerRole;
+                final boolean canChangePowerRole = args[3].endsWith("?");
+                switch (canChangePowerRole ? removeLastChar(args[3]) : args[3]) {
+                    case "source":
+                        powerRole = UsbPort.POWER_ROLE_SOURCE;
+                        break;
+                    case "sink":
+                        powerRole = UsbPort.POWER_ROLE_SINK;
+                        break;
+                    default:
+                        pw.println("Invalid power role: " + args[3]);
+                        return;
+                }
+                final int dataRole;
+                final boolean canChangeDataRole = args[4].endsWith("?");
+                switch (canChangeDataRole ? removeLastChar(args[4]) : args[4]) {
+                    case "host":
+                        dataRole = UsbPort.DATA_ROLE_HOST;
+                        break;
+                    case "device":
+                        dataRole = UsbPort.DATA_ROLE_DEVICE;
+                        break;
+                    default:
+                        pw.println("Invalid data role: " + args[4]);
+                        return;
+                }
+                if (mPortManager != null) {
+                    mPortManager.connectSimulatedPort(portId, mode, canChangeMode,
+                            powerRole, canChangePowerRole, dataRole, canChangeDataRole, pw);
+                    pw.println();
+                    mPortManager.dump(pw);
+                }
+            } else if (args.length == 2 && "disconnect-port".equals(args[0])) {
+                final String portId = args[1];
+                if (mPortManager != null) {
+                    mPortManager.disconnectSimulatedPort(portId, pw);
+                    pw.println();
+                    mPortManager.dump(pw);
+                }
+            } else if (args.length == 2 && "remove-port".equals(args[0])) {
+                final String portId = args[1];
+                if (mPortManager != null) {
+                    mPortManager.removeSimulatedPort(portId, pw);
+                    pw.println();
+                    mPortManager.dump(pw);
+                }
+            } else if (args.length == 1 && "reset".equals(args[0])) {
+                if (mPortManager != null) {
+                    mPortManager.resetSimulation(pw);
+                    pw.println();
+                    mPortManager.dump(pw);
+                }
+            } else if (args.length == 1 && "ports".equals(args[0])) {
+                if (mPortManager != null) {
+                    mPortManager.dump(pw);
+                }
+            } else {
+                pw.println("Dump current USB state or issue command:");
+                pw.println("  ports");
+                pw.println("  set-port-roles <id> <source|sink|no-power> <host|device|no-data>");
+                pw.println("  add-port <id> <ufp|dfp|dual|none>");
+                pw.println("  connect-port <id> <ufp|dfp><?> <source|sink><?> <host|device><?>");
+                pw.println("    (add ? suffix if mode, power role, or data role can be changed)");
+                pw.println("  disconnect-port <id>");
+                pw.println("  remove-port <id>");
+                pw.println("  reset");
+                pw.println();
+                pw.println("Example USB type C port role switch:");
+                pw.println("  dumpsys usb set-port-roles \"default\" source device");
+                pw.println();
+                pw.println("Example USB type C port simulation with full capabilities:");
+                pw.println("  dumpsys usb add-port \"matrix\" dual");
+                pw.println("  dumpsys usb connect-port \"matrix\" ufp? sink? device?");
+                pw.println("  dumpsys usb ports");
+                pw.println("  dumpsys usb disconnect-port \"matrix\"");
+                pw.println("  dumpsys usb remove-port \"matrix\"");
+                pw.println("  dumpsys usb reset");
+                pw.println();
+                pw.println("Example USB type C port where only power role can be changed:");
+                pw.println("  dumpsys usb add-port \"matrix\" dual");
+                pw.println("  dumpsys usb connect-port \"matrix\" dfp source? host");
+                pw.println("  dumpsys usb reset");
+                pw.println();
+                pw.println("Example USB OTG port where id pin determines function:");
+                pw.println("  dumpsys usb add-port \"matrix\" dual");
+                pw.println("  dumpsys usb connect-port \"matrix\" dfp source host");
+                pw.println("  dumpsys usb reset");
+                pw.println();
+                pw.println("Example USB device-only port:");
+                pw.println("  dumpsys usb add-port \"matrix\" ufp");
+                pw.println("  dumpsys usb connect-port \"matrix\" ufp sink device");
+                pw.println("  dumpsys usb reset");
             }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
         }
-        pw.decreaseIndent();
+    }
+
+    private static final String removeLastChar(String value) {
+        return value.substring(0, value.length() - 1);
     }
 }
diff --git a/services/usb/java/com/android/server/usb/UsbSettingsManager.java b/services/usb/java/com/android/server/usb/UsbSettingsManager.java
index 2331a8b..2cf42f0 100644
--- a/services/usb/java/com/android/server/usb/UsbSettingsManager.java
+++ b/services/usb/java/com/android/server/usb/UsbSettingsManager.java
@@ -44,6 +44,7 @@
 
 import com.android.internal.content.PackageMonitor;
 import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.XmlUtils;
 
 import org.xmlpull.v1.XmlPullParser;
@@ -1193,35 +1194,35 @@
         }
     }
 
-    public void dump(FileDescriptor fd, PrintWriter pw) {
+    public void dump(IndentingPrintWriter pw) {
         synchronized (mLock) {
-            pw.println("  Device permissions:");
+            pw.println("Device permissions:");
             for (String deviceName : mDevicePermissionMap.keySet()) {
-                pw.print("    " + deviceName + ": ");
+                pw.print("  " + deviceName + ": ");
                 SparseBooleanArray uidList = mDevicePermissionMap.get(deviceName);
                 int count = uidList.size();
                 for (int i = 0; i < count; i++) {
                     pw.print(Integer.toString(uidList.keyAt(i)) + " ");
                 }
-                pw.println("");
+                pw.println();
             }
-            pw.println("  Accessory permissions:");
+            pw.println("Accessory permissions:");
             for (UsbAccessory accessory : mAccessoryPermissionMap.keySet()) {
-                pw.print("    " + accessory + ": ");
+                pw.print("  " + accessory + ": ");
                 SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory);
                 int count = uidList.size();
                 for (int i = 0; i < count; i++) {
                     pw.print(Integer.toString(uidList.keyAt(i)) + " ");
                 }
-                pw.println("");
+                pw.println();
             }
-            pw.println("  Device preferences:");
+            pw.println("Device preferences:");
             for (DeviceFilter filter : mDevicePreferenceMap.keySet()) {
-                pw.println("    " + filter + ": " + mDevicePreferenceMap.get(filter));
+                pw.println("  " + filter + ": " + mDevicePreferenceMap.get(filter));
             }
-            pw.println("  Accessory preferences:");
+            pw.println("Accessory preferences:");
             for (AccessoryFilter filter : mAccessoryPreferenceMap.keySet()) {
-                pw.println("    " + filter + ": " + mAccessoryPreferenceMap.get(filter));
+                pw.println("  " + filter + ": " + mAccessoryPreferenceMap.get(filter));
             }
         }
     }
diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java
index d18b86a..79146f3 100644
--- a/telephony/java/android/telephony/PhoneNumberUtils.java
+++ b/telephony/java/android/telephony/PhoneNumberUtils.java
@@ -177,15 +177,19 @@
             phoneColumn = ContactsContract.CommonDataKinds.Phone.NUMBER;
         }
 
-        final Cursor c = context.getContentResolver().query(uri, new String[] {
-            phoneColumn
-        }, null, null, null);
-        if (c != null) {
-            try {
+        Cursor c = null;
+        try {
+            c = context.getContentResolver().query(uri, new String[] { phoneColumn },
+                    null, null, null);
+            if (c != null) {
                 if (c.moveToFirst()) {
                     number = c.getString(c.getColumnIndex(phoneColumn));
                 }
-            } finally {
+            }
+        } catch (RuntimeException e) {
+            Rlog.e(LOG_TAG, "Error getting phone number.", e);
+        } finally {
+            if (c != null) {
                 c.close();
             }
         }
diff --git a/telephony/java/com/android/internal/telephony/CallerInfo.java b/telephony/java/com/android/internal/telephony/CallerInfo.java
index 5cd5d4e..be7e702 100644
--- a/telephony/java/com/android/internal/telephony/CallerInfo.java
+++ b/telephony/java/com/android/internal/telephony/CallerInfo.java
@@ -16,6 +16,7 @@
 
 package com.android.internal.telephony;
 
+import android.content.ContentResolver;
 import android.content.Context;
 import android.database.Cursor;
 import android.graphics.Bitmap;
@@ -282,10 +283,17 @@
      * number. The returned CallerInfo is null if no number is supplied.
      */
     public static CallerInfo getCallerInfo(Context context, Uri contactRef) {
-
-        return getCallerInfo(context, contactRef,
-                CallerInfoAsyncQuery.getCurrentProfileContentResolver(context)
-                        .query(contactRef, null, null, null, null));
+        CallerInfo info = null;
+        ContentResolver cr = CallerInfoAsyncQuery.getCurrentProfileContentResolver(context);
+        if (cr != null) {
+            try {
+                info = getCallerInfo(context, contactRef,
+                        cr.query(contactRef, null, null, null, null));
+            } catch (RuntimeException re) {
+                Rlog.e(TAG, "Error getting caller info.", re);
+            }
+        }
+        return info;
     }
 
     /**
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java
index 085df85..895f9c9 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java
@@ -137,7 +137,7 @@
     }
 
     @Override
-    public void wakeUp(long time) throws RemoteException {
+    public void wakeUp(long time, String reason, String opPackageName) throws RemoteException {
         // pass for now.
     }