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/BackupRestoreConfirmation/res/values-fa/strings.xml b/packages/BackupRestoreConfirmation/res/values-fa/strings.xml
index 4c16374..96ef731 100644
--- a/packages/BackupRestoreConfirmation/res/values-fa/strings.xml
+++ b/packages/BackupRestoreConfirmation/res/values-fa/strings.xml
@@ -19,7 +19,7 @@
     <string name="backup_confirm_title" msgid="827563724209303345">"پشتیبان‌گیری کامل"</string>
     <string name="restore_confirm_title" msgid="5469365809567486602">"بازیابی کامل"</string>
     <string name="backup_confirm_text" msgid="1878021282758896593">"درخواست پشتیبان گیری کامل از تمام داده‌ها به یک رایانه دسک‌تاپ متصل داده شده است. آیا می‌خواهید این عمل انجام شود؟\n\nاگر شما درخواست تهیهٔ نسخهٔ پشتیبان را نداده‌اید، اجازه‌ ادامه عملیات را ندهید."</string>
-    <string name="allow_backup_button_label" msgid="4217228747769644068">"از داده‌های من نسخهٔ پشتیبان تهیه شود"</string>
+    <string name="allow_backup_button_label" msgid="4217228747769644068">"پشتیبان‌گیری از داده‌های من"</string>
     <string name="deny_backup_button_label" msgid="6009119115581097708">"نسخهٔ پشتیبان تهیه نشود"</string>
     <string name="restore_confirm_text" msgid="7499866728030461776">"بازیابی کامل تمام داده‌ها از یک رایانه دسک تاپ متصل درخواست شده است. آیا می‌خواهید این اجازه را بدهید؟\n\nاگر خود شما درخواست بازیابی نداده‌اید، اجازه ادامه این عملیات را ندهید. با این کار همه داده‌هایی که اکنون روی دستگاه است جایگزین می‌شود!"</string>
     <string name="allow_restore_button_label" msgid="3081286752277127827">"بازیابی داده‌های من"</string>
diff --git a/packages/BackupRestoreConfirmation/res/values-hr/strings.xml b/packages/BackupRestoreConfirmation/res/values-hr/strings.xml
index 66037f3..cda36cb 100644
--- a/packages/BackupRestoreConfirmation/res/values-hr/strings.xml
+++ b/packages/BackupRestoreConfirmation/res/values-hr/strings.xml
@@ -19,7 +19,7 @@
     <string name="backup_confirm_title" msgid="827563724209303345">"Puna sigurnosna kopija"</string>
     <string name="restore_confirm_title" msgid="5469365809567486602">"Potpuno vraćanje"</string>
     <string name="backup_confirm_text" msgid="1878021282758896593">"Zatražena je potpuna sigurnosna kopija svih podataka na povezano stolno računalo. Želite li to dozvoliti?\n\nAko niste vi zatražili sigurnosnu kopiju, ne dozvolite nastavak te radnje."</string>
-    <string name="allow_backup_button_label" msgid="4217228747769644068">"Izradi sigurnosnu kopiju mojih podataka"</string>
+    <string name="allow_backup_button_label" msgid="4217228747769644068">"Sigurnosno kopiranje"</string>
     <string name="deny_backup_button_label" msgid="6009119115581097708">"Ne radi sigurnosnu kopiju"</string>
     <string name="restore_confirm_text" msgid="7499866728030461776">"Zatraženo je potpuno vraćanje svih podataka s povezanog stolnog računala. Želite li to dozvoliti?\n\nAko niste sami zatražili vraćanje, ne dozvolite nastavak radnje. To će zamijeniti sve podatke koji se trenutačno nalaze na uređaju!"</string>
     <string name="allow_restore_button_label" msgid="3081286752277127827">"Vrati moje podatke"</string>
@@ -28,8 +28,8 @@
     <string name="device_encryption_restore_text" msgid="1570864916855208992">"U nastavku unesite svoju zaporku za enkripciju uređaja."</string>
     <string name="device_encryption_backup_text" msgid="5866590762672844664">"U nastavku unesite svoju zaporku enkripcije za uređaj. Ona će se upotrijebiti i za enkripciju te za arhivu sigurnosnih kopija."</string>
     <string name="backup_enc_password_text" msgid="4981585714795233099">"Unesite zaporku koju ćete upotrebljavati za kriptiranje podataka potpune sigurnosne kopije. Ako je ostavite praznom, bit će upotrijebljena vaša trenutačna zaporka za sigurnosno kopiranje:"</string>
-    <string name="backup_enc_password_optional" msgid="1350137345907579306">"Ako želite kriptirati podatke potpune sigurnosne kopije, u nastavku unesite zaporku:"</string>
-    <string name="backup_enc_password_required" msgid="7889652203371654149">"Budući da vam je uređaj kriptiran, morate kriptirati sigurnosne kopije. Unesite zaporku u nastavku:"</string>
+    <string name="backup_enc_password_optional" msgid="1350137345907579306">"Ako želite šifrirati podatke potpune sigurnosne kopije, u nastavku unesite zaporku:"</string>
+    <string name="backup_enc_password_required" msgid="7889652203371654149">"Budući da vam je uređaj kriptiran, morate šifrirati sigurnosne kopije. Unesite zaporku u nastavku:"</string>
     <string name="restore_enc_password_text" msgid="6140898525580710823">"Ako su podaci za vraćanje kriptirani, unesite zaporku u nastavku:"</string>
     <string name="toast_backup_started" msgid="550354281452756121">"Započinje stvaranje sigurnosne kopije..."</string>
     <string name="toast_backup_ended" msgid="3818080769548726424">"Sigurnosna kopija dovršena"</string>
diff --git a/packages/BackupRestoreConfirmation/res/values-iw/strings.xml b/packages/BackupRestoreConfirmation/res/values-iw/strings.xml
index 8c2ffaf..4c13c73 100644
--- a/packages/BackupRestoreConfirmation/res/values-iw/strings.xml
+++ b/packages/BackupRestoreConfirmation/res/values-iw/strings.xml
@@ -19,7 +19,7 @@
     <string name="backup_confirm_title" msgid="827563724209303345">"גיבוי מלא"</string>
     <string name="restore_confirm_title" msgid="5469365809567486602">"שחזור מלא"</string>
     <string name="backup_confirm_text" msgid="1878021282758896593">"הוגשה בקשה לגיבוי מלא של כל הנתונים במחשב שולחני מחובר. האם אתה רוצה לאפשר פעולה זו? \n\nאם לא ביקשת את הגיבוי בעצמך, אל תאפשר לפעולה להמשיך."</string>
-    <string name="allow_backup_button_label" msgid="4217228747769644068">"גבה את הנתונים שלי"</string>
+    <string name="allow_backup_button_label" msgid="4217228747769644068">"גיבוי הנתונים שלי"</string>
     <string name="deny_backup_button_label" msgid="6009119115581097708">"אל תגבה"</string>
     <string name="restore_confirm_text" msgid="7499866728030461776">"הוגשה בקשה לשחזור מלא של כל הנתונים ממחשב שולחני מחובר. האם אתה רוצה לאפשר פעולה זו? \n \n אם לא ביקשת את השחזור בעצמך, אל תאפשר לפעולה להמשיך. פעולה זו תחליף את כל הנתונים שנמצאים כעת במכשיר!"</string>
     <string name="allow_restore_button_label" msgid="3081286752277127827">"שחזר את הנתונים שלי"</string>
diff --git a/packages/BackupRestoreConfirmation/res/values-ml-rIN/strings.xml b/packages/BackupRestoreConfirmation/res/values-ml-rIN/strings.xml
index b2b4bcb..5f97afa 100644
--- a/packages/BackupRestoreConfirmation/res/values-ml-rIN/strings.xml
+++ b/packages/BackupRestoreConfirmation/res/values-ml-rIN/strings.xml
@@ -19,7 +19,7 @@
     <string name="backup_confirm_title" msgid="827563724209303345">"പൂർണ്ണ ബാക്കപ്പ്"</string>
     <string name="restore_confirm_title" msgid="5469365809567486602">"പൂർണ്ണമായി പുനഃസ്ഥാപിക്കൽ"</string>
     <string name="backup_confirm_text" msgid="1878021282758896593">"കണക്റ്റുചെയ്‌ത ഡെസ്‌ക്‌ടോപ്പ് കമ്പ്യൂട്ടറിലേക്കുള്ള എല്ലാ ഡാറ്റയുടെയും പൂർണ്ണ ബാക്കപ്പ് ആവശ്യപ്പെട്ടു. ഇത് സംഭവിക്കാൻ അനുവദിക്കണോ?\n\nനിങ്ങൾ സ്വയം ബാക്കപ്പുചെയ്യാൻ ആവശ്യപ്പെട്ടില്ലെങ്കിൽ, ഈ പ്രവർത്തനം തുടരാൻ അനുവദിക്കരുത്."</string>
-    <string name="allow_backup_button_label" msgid="4217228747769644068">"എന്റെ ഡാറ്റ ബാക്കപ്പുചെയ്യുക"</string>
+    <string name="allow_backup_button_label" msgid="4217228747769644068">"ഡാറ്റ ബാക്കപ്പുചെയ്യൂ"</string>
     <string name="deny_backup_button_label" msgid="6009119115581097708">"ബാക്കപ്പ് ചെയ്യരുത്"</string>
     <string name="restore_confirm_text" msgid="7499866728030461776">"കണക്റ്റുചെയ്‌ത ഡെസ്‌ക്‌ടോപ്പ് കമ്പ്യൂട്ടറിലേക്കുള്ള എല്ലാ ഡാറ്റയുടെയും പൂർണ്ണ ബാക്കപ്പ് ആവശ്യപ്പെട്ടു. ഇത് സംഭവിക്കാൻ അനുവദിക്കണോ?\n\nനിങ്ങൾ സ്വയം ബാക്കപ്പുചെയ്യാൻ ആവശ്യപ്പെട്ടില്ലെങ്കിൽ, ഈ പ്രവർത്തനം തുടരാൻ അനുവദിക്കരുത്. ഇത് ഉപകരണത്തിൽ നിലവിലുള്ള എല്ലാ ഡാറ്റയേയും മാറ്റി പകരം വയ്ക്കും!"</string>
     <string name="allow_restore_button_label" msgid="3081286752277127827">"എന്റെ ഡാറ്റ പുനഃസ്ഥാപിക്കുക"</string>
diff --git a/packages/BackupRestoreConfirmation/res/values-sk/strings.xml b/packages/BackupRestoreConfirmation/res/values-sk/strings.xml
index a231d23..804f980 100644
--- a/packages/BackupRestoreConfirmation/res/values-sk/strings.xml
+++ b/packages/BackupRestoreConfirmation/res/values-sk/strings.xml
@@ -19,7 +19,7 @@
     <string name="backup_confirm_title" msgid="827563724209303345">"Úplná záloha"</string>
     <string name="restore_confirm_title" msgid="5469365809567486602">"Úplné obnovenie"</string>
     <string name="backup_confirm_text" msgid="1878021282758896593">"Bola vyžiadaná úplná záloha všetkých dát do pripojeného počítača. Chcete túto akciu povoliť?\n\nAk ste zálohu nevyžiadali vy, túto operáciu nepovoľujte."</string>
-    <string name="allow_backup_button_label" msgid="4217228747769644068">"Zálohovať údaje"</string>
+    <string name="allow_backup_button_label" msgid="4217228747769644068">"Zálohovať dáta"</string>
     <string name="deny_backup_button_label" msgid="6009119115581097708">"Nezálohovať"</string>
     <string name="restore_confirm_text" msgid="7499866728030461776">"Z pripojeného počítača bolo vyžiadané úplné obnovenie všetkých údajov. Chcete túto akciu povoliť?\n\nAk ste toto obnovenie nevyžiadali vy, túto operáciu nepovoľujte. Táto akcia nahradí všetky údaje v zariadení."</string>
     <string name="allow_restore_button_label" msgid="3081286752277127827">"Obnoviť údaje"</string>
diff --git a/packages/BackupRestoreConfirmation/res/values-sv/strings.xml b/packages/BackupRestoreConfirmation/res/values-sv/strings.xml
index 3ae37cf..dee8bc2 100644
--- a/packages/BackupRestoreConfirmation/res/values-sv/strings.xml
+++ b/packages/BackupRestoreConfirmation/res/values-sv/strings.xml
@@ -19,7 +19,7 @@
     <string name="backup_confirm_title" msgid="827563724209303345">"Fullständig säkerhetskopiering"</string>
     <string name="restore_confirm_title" msgid="5469365809567486602">"Fullständig återställning"</string>
     <string name="backup_confirm_text" msgid="1878021282758896593">"En fullständig säkerhetskopia av alla data till en ansluten dator har begärts. Vill du tillåta detta?\n\nOm du inte själv begärde säkerhetskopian ska du inte tillåta detta."</string>
-    <string name="allow_backup_button_label" msgid="4217228747769644068">"Säkerhetskopiera mina data"</string>
+    <string name="allow_backup_button_label" msgid="4217228747769644068">"Säkerhetskopiera data"</string>
     <string name="deny_backup_button_label" msgid="6009119115581097708">"Säkerhetskopiera inte"</string>
     <string name="restore_confirm_text" msgid="7499866728030461776">"En fullständig återställning av alla data från en ansluten dator har begärts. Vill du tillåta detta? \n \n Om du inte själv har begärt återställningen ska du inte tillåta den. Alla data som finns på enheten kommer då att ersättas!"</string>
     <string name="allow_restore_button_label" msgid="3081286752277127827">"Återställ mina data"</string>
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/DefaultContainerService/res/values-fr/strings.xml b/packages/DefaultContainerService/res/values-fr/strings.xml
index 5c458bc..216d715 100644
--- a/packages/DefaultContainerService/res/values-fr/strings.xml
+++ b/packages/DefaultContainerService/res/values-fr/strings.xml
@@ -20,5 +20,5 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="service_name" msgid="4841491635055379553">"Aide accès au package"</string>
+    <string name="service_name" msgid="4841491635055379553">"Package Access Helper"</string>
 </resources>
diff --git a/packages/DefaultContainerService/res/values-pt-rPT/strings.xml b/packages/DefaultContainerService/res/values-pt-rPT/strings.xml
index 5c03669..8ea6a3a 100644
--- a/packages/DefaultContainerService/res/values-pt-rPT/strings.xml
+++ b/packages/DefaultContainerService/res/values-pt-rPT/strings.xml
@@ -20,5 +20,5 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="service_name" msgid="4841491635055379553">"Ajuda p/ aceder pacotes"</string>
+    <string name="service_name" msgid="4841491635055379553">"Ajuda p/ aceder a pacotes"</string>
 </resources>
diff --git a/packages/DocumentsUI/res/values-gu-rIN/strings.xml b/packages/DocumentsUI/res/values-gu-rIN/strings.xml
index a76c490..0281ba9 100644
--- a/packages/DocumentsUI/res/values-gu-rIN/strings.xml
+++ b/packages/DocumentsUI/res/values-gu-rIN/strings.xml
@@ -31,18 +31,18 @@
     <string name="menu_delete" msgid="8138799623850614177">"કાઢી નાખો"</string>
     <string name="menu_select_all" msgid="8323579667348729928">"બધા પસંદ કરો"</string>
     <string name="menu_copy" msgid="3612326052677229148">"આના પર કૉપિ કરો…"</string>
-    <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"આંતરિક સ્ટોરેજ દર્શાવો"</string>
-    <string name="menu_advanced_show" product="default" msgid="5792182900084144261">"SD કાર્ડ દર્શાવો"</string>
+    <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"આંતરિક સ્ટોરેજ બતાવો"</string>
+    <string name="menu_advanced_show" product="default" msgid="5792182900084144261">"SD કાર્ડ બતાવો"</string>
     <string name="menu_advanced_hide" product="nosdcard" msgid="4218809952721972589">"આંતરિક સંગ્રહ છુપાવો"</string>
     <string name="menu_advanced_hide" product="default" msgid="4845869969015718848">"SD કાર્ડ છુપાવો"</string>
-    <string name="menu_file_size_show" msgid="3240323619260823076">"ફાઇલ કદ દર્શાવો"</string>
+    <string name="menu_file_size_show" msgid="3240323619260823076">"ફાઇલ કદ બતાવો"</string>
     <string name="menu_file_size_hide" msgid="8881975928502581042">"ફાઇલ કદ છુપાવો"</string>
     <string name="button_select" msgid="527196987259139214">"પસંદ કરો"</string>
     <string name="button_copy" msgid="8706475544635021302">"કૉપિ કરો"</string>
     <string name="sort_name" msgid="9183560467917256779">"નામ દ્વારા"</string>
     <string name="sort_date" msgid="586080032956151448">"સંશોધન તારીખ દ્વારા"</string>
     <string name="sort_size" msgid="3350681319735474741">"કદ દ્વારા"</string>
-    <string name="drawer_open" msgid="4545466532430226949">"રૂટ્સ દર્શાવો"</string>
+    <string name="drawer_open" msgid="4545466532430226949">"રૂટ્સ બતાવો"</string>
     <string name="drawer_close" msgid="7602734368552123318">"રૂટ્સ છુપાવો"</string>
     <string name="save_error" msgid="6167009778003223664">"દસ્તાવેજ સાચવવામાં નિષ્ફળ થયાં."</string>
     <string name="create_error" msgid="3735649141335444215">"ફોલ્ડર બનાવવામાં નિષ્ફળ થયા"</string>
@@ -52,7 +52,7 @@
     <string name="root_type_service" msgid="2178854894416775409">"સંગ્રહ સેવાઓ"</string>
     <string name="root_type_shortcut" msgid="3318760609471618093">"શોર્ટકટ્સ"</string>
     <string name="root_type_device" msgid="7121342474653483538">"ઉપકરણો"</string>
-    <string name="root_type_apps" msgid="8838065367985945189">"વધુ એપ્લિકેશન્સ"</string>
+    <string name="root_type_apps" msgid="8838065367985945189">"વધુ એપ્લિકેશનો"</string>
     <string name="empty" msgid="7858882803708117596">"કોઈ આઇટમ્સ નથી"</string>
     <string name="toast_no_application" msgid="1339885974067891667">"ફાઇલ ખોલી શકાતી નથી"</string>
     <string name="toast_failed_delete" msgid="2180678019407244069">"કેટલાક દસ્તાવેજો કાઢી નાખવામાં અસમર્થ"</string>
diff --git a/packages/DocumentsUI/res/values-ml-rIN/strings.xml b/packages/DocumentsUI/res/values-ml-rIN/strings.xml
index 56b22b1..4c4b481 100644
--- a/packages/DocumentsUI/res/values-ml-rIN/strings.xml
+++ b/packages/DocumentsUI/res/values-ml-rIN/strings.xml
@@ -31,9 +31,9 @@
     <string name="menu_delete" msgid="8138799623850614177">"ഇല്ലാതാക്കുക"</string>
     <string name="menu_select_all" msgid="8323579667348729928">"എല്ലാം തിരഞ്ഞെടുക്കുക"</string>
     <string name="menu_copy" msgid="3612326052677229148">"ഇതിൽ പകർത്തുക…"</string>
-    <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"ആന്തരിക സംഭരണം കാണിക്കുക"</string>
+    <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"ആന്തരിക സ്റ്റോറേജ്  കാണിക്കുക"</string>
     <string name="menu_advanced_show" product="default" msgid="5792182900084144261">"SD കാർഡ് കാണിക്കുക"</string>
-    <string name="menu_advanced_hide" product="nosdcard" msgid="4218809952721972589">"ആന്തരിക സംഭരണം മറയ്‌ക്കുക"</string>
+    <string name="menu_advanced_hide" product="nosdcard" msgid="4218809952721972589">"ആന്തരിക സ്റ്റോറേജ്  മറയ്‌ക്കുക"</string>
     <string name="menu_advanced_hide" product="default" msgid="4845869969015718848">"SD കാർഡ് മറയ്‌ക്കുക"</string>
     <string name="menu_file_size_show" msgid="3240323619260823076">"ഫയൽ വലുപ്പം കാണിക്കുക"</string>
     <string name="menu_file_size_hide" msgid="8881975928502581042">"ഫയൽ വലുപ്പം മറയ്‌ക്കുക"</string>
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/ExternalStorageProvider/res/values-da/strings.xml b/packages/ExternalStorageProvider/res/values-da/strings.xml
index a9ecb69..dc565ae 100644
--- a/packages/ExternalStorageProvider/res/values-da/strings.xml
+++ b/packages/ExternalStorageProvider/res/values-da/strings.xml
@@ -16,7 +16,7 @@
 
 <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="7123375275748530234">"Ekstern lagring"</string>
-    <string name="root_internal_storage" msgid="827844243068584127">"Intern lagring"</string>
+    <string name="app_label" msgid="7123375275748530234">"Ekstern lagerplads"</string>
+    <string name="root_internal_storage" msgid="827844243068584127">"Intern lagerplads"</string>
     <string name="root_documents" msgid="4051252304075469250">"Dokumenter"</string>
 </resources>
diff --git a/packages/ExternalStorageProvider/res/values-ml-rIN/strings.xml b/packages/ExternalStorageProvider/res/values-ml-rIN/strings.xml
index 204b336..08e6dae 100644
--- a/packages/ExternalStorageProvider/res/values-ml-rIN/strings.xml
+++ b/packages/ExternalStorageProvider/res/values-ml-rIN/strings.xml
@@ -16,7 +16,7 @@
 
 <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="7123375275748530234">"ബാഹ്യ സംഭരണം"</string>
-    <string name="root_internal_storage" msgid="827844243068584127">"ആന്തരിക സംഭരണം"</string>
+    <string name="app_label" msgid="7123375275748530234">"ബാഹ്യ സ്റ്റോറേജ്"</string>
+    <string name="root_internal_storage" msgid="827844243068584127">"ആന്തരിക സ്റ്റോറേജ്"</string>
     <string name="root_documents" msgid="4051252304075469250">"പ്രമാണങ്ങൾ"</string>
 </resources>
diff --git a/packages/ExternalStorageProvider/res/values-ru/strings.xml b/packages/ExternalStorageProvider/res/values-ru/strings.xml
index b6c10951..740272f 100644
--- a/packages/ExternalStorageProvider/res/values-ru/strings.xml
+++ b/packages/ExternalStorageProvider/res/values-ru/strings.xml
@@ -17,6 +17,6 @@
 <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="7123375275748530234">"Внешний накопитель"</string>
-    <string name="root_internal_storage" msgid="827844243068584127">"Внутренняя память"</string>
+    <string name="root_internal_storage" msgid="827844243068584127">"Внутренний накопитель"</string>
     <string name="root_documents" msgid="4051252304075469250">"Документы"</string>
 </resources>
diff --git a/packages/InputDevices/res/values-uk/strings.xml b/packages/InputDevices/res/values-uk/strings.xml
index d8152d4..43a3fe6 100644
--- a/packages/InputDevices/res/values-uk/strings.xml
+++ b/packages/InputDevices/res/values-uk/strings.xml
@@ -3,7 +3,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="8016145283189546017">"Пристрої вводу"</string>
     <string name="keyboard_layouts_label" msgid="6688773268302087545">"Клавіатура Android"</string>
-    <string name="keyboard_layout_english_uk_label" msgid="6664258463319999632">"англійська (Великобританія)"</string>
+    <string name="keyboard_layout_english_uk_label" msgid="6664258463319999632">"англійська (Велика Британія)"</string>
     <string name="keyboard_layout_english_us_label" msgid="8994890249649106291">"англійська (США)"</string>
     <string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"англійська (США), міжнародна"</string>
     <string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"англійська (США), розкладка Colemak"</string>
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 458928f6..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.
     }