Merge "Switch Android Keystore key gen and import to new KeyStore API." into mnc-dev
diff --git a/api/current.txt b/api/current.txt
index 0228d49..32af4c1 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5790,7 +5790,6 @@
     method public void setPermissionPolicy(android.content.ComponentName, int);
     method public boolean setPermittedAccessibilityServices(android.content.ComponentName, java.util.List<java.lang.String>);
     method public boolean setPermittedInputMethods(android.content.ComponentName, java.util.List<java.lang.String>);
-    method public void setPreferredSetupActivity(android.content.ComponentName, android.content.ComponentName);
     method public void setProfileEnabled(android.content.ComponentName);
     method public void setProfileName(android.content.ComponentName, java.lang.String);
     method public void setRecommendedGlobalProxy(android.content.ComponentName, android.net.ProxyInfo);
@@ -15243,6 +15242,7 @@
     field public static final int ORIENTATION_UNDEFINED = 0; // 0x0
     field public static final java.lang.String TAG_APERTURE = "FNumber";
     field public static final java.lang.String TAG_DATETIME = "DateTime";
+    field public static final java.lang.String TAG_DATETIME_DIGITIZED = "DateTimeDigitized";
     field public static final java.lang.String TAG_EXPOSURE_TIME = "ExposureTime";
     field public static final java.lang.String TAG_FLASH = "Flash";
     field public static final java.lang.String TAG_FOCAL_LENGTH = "FocalLength";
@@ -15261,6 +15261,9 @@
     field public static final java.lang.String TAG_MAKE = "Make";
     field public static final java.lang.String TAG_MODEL = "Model";
     field public static final java.lang.String TAG_ORIENTATION = "Orientation";
+    field public static final java.lang.String TAG_SUBSEC_TIME = "SubSecTime";
+    field public static final java.lang.String TAG_SUBSEC_TIME_DIG = "SubSecTimeDigitized";
+    field public static final java.lang.String TAG_SUBSEC_TIME_ORIG = "SubSecTimeOriginal";
     field public static final java.lang.String TAG_WHITE_BALANCE = "WhiteBalance";
     field public static final int WHITEBALANCE_AUTO = 0; // 0x0
     field public static final int WHITEBALANCE_MANUAL = 1; // 0x1
@@ -22900,6 +22903,8 @@
   public static class Debug.MemoryInfo implements android.os.Parcelable {
     ctor public Debug.MemoryInfo();
     method public int describeContents();
+    method public java.lang.String getMemoryStat(java.lang.String);
+    method public java.util.Map<java.lang.String, java.lang.String> getMemoryStats();
     method public int getTotalPrivateClean();
     method public int getTotalPrivateDirty();
     method public int getTotalPss();
@@ -23446,6 +23451,7 @@
     method public static final int getGidForName(java.lang.String);
     method public static final int getThreadPriority(int) throws java.lang.IllegalArgumentException;
     method public static final int getUidForName(java.lang.String);
+    method public static final boolean is64Bit();
     method public static final void killProcess(int);
     method public static final int myPid();
     method public static final int myTid();
@@ -30604,8 +30610,8 @@
     field public static final java.lang.String EXTRA_CALL_DISCONNECT_CAUSE = "android.telecom.extra.CALL_DISCONNECT_CAUSE";
     field public static final java.lang.String EXTRA_CALL_DISCONNECT_MESSAGE = "android.telecom.extra.CALL_DISCONNECT_MESSAGE";
     field public static final java.lang.String EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME = "android.telecom.extra.CHANGE_DEFAULT_DIALER_PACKAGE_NAME";
+    field public static final java.lang.String EXTRA_INCOMING_CALL_ADDRESS = "android.telecom.extra.INCOMING_CALL_ADDRESS";
     field public static final java.lang.String EXTRA_INCOMING_CALL_EXTRAS = "android.telecom.extra.INCOMING_CALL_EXTRAS";
-    field public static final java.lang.String EXTRA_INCOMING_CALL_HANDLE = "android.telecom.extra.INCOMING_CALL_HANDLE";
     field public static final java.lang.String EXTRA_OUTGOING_CALL_EXTRAS = "android.telecom.extra.OUTGOING_CALL_EXTRAS";
     field public static final java.lang.String EXTRA_PHONE_ACCOUNT_HANDLE = "android.telecom.extra.PHONE_ACCOUNT_HANDLE";
     field public static final java.lang.String EXTRA_START_CALL_WITH_SPEAKERPHONE = "android.telecom.extra.START_CALL_WITH_SPEAKERPHONE";
diff --git a/api/system-current.txt b/api/system-current.txt
index 0d7a5b3..cff5c99 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -5900,7 +5900,6 @@
     method public void setPermissionPolicy(android.content.ComponentName, int);
     method public boolean setPermittedAccessibilityServices(android.content.ComponentName, java.util.List<java.lang.String>);
     method public boolean setPermittedInputMethods(android.content.ComponentName, java.util.List<java.lang.String>);
-    method public void setPreferredSetupActivity(android.content.ComponentName, android.content.ComponentName);
     method public void setProfileEnabled(android.content.ComponentName);
     method public void setProfileName(android.content.ComponentName, java.lang.String);
     method public void setRecommendedGlobalProxy(android.content.ComponentName, android.net.ProxyInfo);
@@ -16484,6 +16483,7 @@
     field public static final int ORIENTATION_UNDEFINED = 0; // 0x0
     field public static final java.lang.String TAG_APERTURE = "FNumber";
     field public static final java.lang.String TAG_DATETIME = "DateTime";
+    field public static final java.lang.String TAG_DATETIME_DIGITIZED = "DateTimeDigitized";
     field public static final java.lang.String TAG_EXPOSURE_TIME = "ExposureTime";
     field public static final java.lang.String TAG_FLASH = "Flash";
     field public static final java.lang.String TAG_FOCAL_LENGTH = "FocalLength";
@@ -16502,6 +16502,9 @@
     field public static final java.lang.String TAG_MAKE = "Make";
     field public static final java.lang.String TAG_MODEL = "Model";
     field public static final java.lang.String TAG_ORIENTATION = "Orientation";
+    field public static final java.lang.String TAG_SUBSEC_TIME = "SubSecTime";
+    field public static final java.lang.String TAG_SUBSEC_TIME_DIG = "SubSecTimeDigitized";
+    field public static final java.lang.String TAG_SUBSEC_TIME_ORIG = "SubSecTimeOriginal";
     field public static final java.lang.String TAG_WHITE_BALANCE = "WhiteBalance";
     field public static final int WHITEBALANCE_AUTO = 0; // 0x0
     field public static final int WHITEBALANCE_MANUAL = 1; // 0x1
@@ -24818,6 +24821,8 @@
   public static class Debug.MemoryInfo implements android.os.Parcelable {
     ctor public Debug.MemoryInfo();
     method public int describeContents();
+    method public java.lang.String getMemoryStat(java.lang.String);
+    method public java.util.Map<java.lang.String, java.lang.String> getMemoryStats();
     method public int getTotalPrivateClean();
     method public int getTotalPrivateDirty();
     method public int getTotalPss();
@@ -25372,6 +25377,7 @@
     method public static final int getGidForName(java.lang.String);
     method public static final int getThreadPriority(int) throws java.lang.IllegalArgumentException;
     method public static final int getUidForName(java.lang.String);
+    method public static final boolean is64Bit();
     method public static final void killProcess(int);
     method public static final int myPid();
     method public static final int myTid();
@@ -32818,8 +32824,8 @@
     field public static final java.lang.String EXTRA_CALL_DISCONNECT_MESSAGE = "android.telecom.extra.CALL_DISCONNECT_MESSAGE";
     field public static final java.lang.String EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME = "android.telecom.extra.CHANGE_DEFAULT_DIALER_PACKAGE_NAME";
     field public static final java.lang.String EXTRA_CONNECTION_SERVICE = "android.telecom.extra.CONNECTION_SERVICE";
+    field public static final java.lang.String EXTRA_INCOMING_CALL_ADDRESS = "android.telecom.extra.INCOMING_CALL_ADDRESS";
     field public static final java.lang.String EXTRA_INCOMING_CALL_EXTRAS = "android.telecom.extra.INCOMING_CALL_EXTRAS";
-    field public static final java.lang.String EXTRA_INCOMING_CALL_HANDLE = "android.telecom.extra.INCOMING_CALL_HANDLE";
     field public static final java.lang.String EXTRA_OUTGOING_CALL_EXTRAS = "android.telecom.extra.OUTGOING_CALL_EXTRAS";
     field public static final java.lang.String EXTRA_PHONE_ACCOUNT_HANDLE = "android.telecom.extra.PHONE_ACCOUNT_HANDLE";
     field public static final java.lang.String EXTRA_START_CALL_WITH_SPEAKERPHONE = "android.telecom.extra.START_CALL_WITH_SPEAKERPHONE";
diff --git a/core/java/android/animation/AnimatorInflater.java b/core/java/android/animation/AnimatorInflater.java
index 435d5ab..d8d2737 100644
--- a/core/java/android/animation/AnimatorInflater.java
+++ b/core/java/android/animation/AnimatorInflater.java
@@ -441,8 +441,12 @@
 
         long startDelay = arrayAnimator.getInt(R.styleable.Animator_startOffset, 0);
 
-        int valueType = arrayAnimator.getInt(R.styleable.Animator_valueType, VALUE_TYPE_FLOAT);
+        int valueType = arrayAnimator.getInt(R.styleable.Animator_valueType, VALUE_TYPE_UNDEFINED);
 
+        if (valueType == VALUE_TYPE_UNDEFINED) {
+            valueType = inferValueTypeFromValues(arrayAnimator, R.styleable.Animator_valueFrom,
+                    R.styleable.Animator_valueTo);
+        }
         PropertyValuesHolder pvh = getPVH(arrayAnimator, valueType,
                 R.styleable.Animator_valueFrom, R.styleable.Animator_valueTo, "");
         if (pvh != null) {
@@ -520,8 +524,14 @@
         ObjectAnimator oa = (ObjectAnimator) anim;
         String pathData = arrayObjectAnimator.getString(R.styleable.PropertyAnimator_pathData);
 
-        // Note that if there is a pathData defined in the Object Animator,
-        // valueFrom / valueTo will be ignored.
+        // Path can be involved in an ObjectAnimator in the following 3 ways:
+        // 1) Path morphing: the property to be animated is pathData, and valueFrom and valueTo
+        //    are both of pathType. valueType = pathType needs to be explicitly defined.
+        // 2) A property in X or Y dimension can be animated along a path: the property needs to be
+        //    defined in propertyXName or propertyYName attribute, the path will be defined in the
+        //    pathData attribute. valueFrom and valueTo will not be necessary for this animation.
+        // 3) PathInterpolator can also define a path (in pathData) for its interpolation curve.
+        // Here we are dealing with case 2:
         if (pathData != null) {
             String propertyXName =
                     arrayObjectAnimator.getString(R.styleable.PropertyAnimator_propertyXName);
@@ -805,6 +815,25 @@
         return valueType;
     }
 
+    private static int inferValueTypeFromValues(TypedArray styledAttributes, int valueFromId,
+            int valueToId) {
+        TypedValue tvFrom = styledAttributes.peekValue(valueFromId);
+        boolean hasFrom = (tvFrom != null);
+        int fromType = hasFrom ? tvFrom.type : 0;
+        TypedValue tvTo = styledAttributes.peekValue(valueToId);
+        boolean hasTo = (tvTo != null);
+        int toType = hasTo ? tvTo.type : 0;
+
+        int valueType;
+        // Check whether it's color type. If not, fall back to default type (i.e. float type)
+        if ((hasFrom && isColorType(fromType)) || (hasTo && isColorType(toType))) {
+            valueType = VALUE_TYPE_COLOR;
+        } else {
+            valueType = VALUE_TYPE_FLOAT;
+        }
+        return valueType;
+    }
+
     private static void dumpKeyframes(Object[] keyframes, String header) {
         if (keyframes == null || keyframes.length == 0) {
             return;
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index bb553e4..dabcc50c 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -2534,15 +2534,6 @@
             return true;
         }
 
-        case UPDATE_PREFERRED_SETUP_ACTIVITY_TRANSACTION: {
-            data.enforceInterface(IActivityManager.descriptor);
-            ComponentName preferredActivity = ComponentName.readFromParcel(data);
-            int userId = data.readInt();
-            updatePreferredSetupActivity(preferredActivity, userId);
-            reply.writeNoException();
-            return true;
-        }
-
         case GET_PACKAGE_PROCESS_STATE_TRANSACTION: {
             data.enforceInterface(IActivityManager.descriptor);
             String pkg = data.readString();
@@ -5868,20 +5859,6 @@
     }
 
     @Override
-    public void updatePreferredSetupActivity(ComponentName preferredActivity, int userId)
-            throws RemoteException {
-        Parcel data = Parcel.obtain();
-        Parcel reply = Parcel.obtain();
-        data.writeInterfaceToken(IActivityManager.descriptor);
-        ComponentName.writeToParcel(preferredActivity, data);
-        data.writeInt(userId);
-        mRemote.transact(UPDATE_PREFERRED_SETUP_ACTIVITY_TRANSACTION, data, reply, 0);
-        reply.readException();
-        data.recycle();
-        reply.recycle();
-    }
-
-    @Override
     public int getPackageProcessState(String packageName) throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index e87eabe..0d5e1c7 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -502,8 +502,6 @@
             throws RemoteException;
     public void updateLockTaskPackages(int userId, String[] packages) throws RemoteException;
     public void updateDeviceOwner(String packageName) throws RemoteException;
-    public void updatePreferredSetupActivity(ComponentName preferredActivity, int userId)
-            throws RemoteException;
 
     public int getPackageProcessState(String packageName) throws RemoteException;
 
@@ -850,8 +848,7 @@
     int GET_PACKAGE_PROCESS_STATE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+293;
     int SHOW_LOCK_TASK_ESCAPE_MESSAGE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+294;
     int UPDATE_DEVICE_OWNER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+295;
-    int UPDATE_PREFERRED_SETUP_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+296;
-    int KEYGUARD_GOING_AWAY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+297;
-    int REGISTER_UID_OBSERVER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+298;
-    int UNREGISTER_UID_OBSERVER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+299;
+    int KEYGUARD_GOING_AWAY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+296;
+    int REGISTER_UID_OBSERVER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+297;
+    int UNREGISTER_UID_OBSERVER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+298;
 }
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 55eaf27..3ab0e01 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -4404,24 +4404,6 @@
     }
 
     /**
-     * Called by a device initializer to set the activity to be launched on device boot or after a
-     * user switch during user setup. This activity will be started regardless of the priority of
-     * other 'home' activities. Once user setup is complete, the preferred setup activity will be
-     * ignored.
-     *
-     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
-     * @param activity The Activity to be started by default during user setup.
-     */
-    public void setPreferredSetupActivity(@NonNull ComponentName admin,
-            @NonNull ComponentName activity) {
-        try {
-            mService.setPreferredSetupActivity(admin, activity);
-        } catch (RemoteException re) {
-            Log.w(TAG, "Failed talking with device policy service", re);
-        }
-    }
-
-    /**
      * Called by profile or device owners to set the default response for future runtime permission
      * requests by applications. The policy can allow for normal operation which prompts the
      * user to grant a permission, or can allow automatic granting or denying of runtime
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 477a3384..8c7b20a 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -218,8 +218,6 @@
     String getDeviceInitializer();
     ComponentName getDeviceInitializerComponent();
 
-    void setPreferredSetupActivity(in ComponentName admin, in ComponentName activity);
-
     void setUserIcon(in ComponentName admin, in Bitmap icon);
 
     void sendDeviceInitializerStatus(int statusCode, String description);
diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java
index 19c8fa9..87e8c5e 100644
--- a/core/java/android/os/Debug.java
+++ b/core/java/android/os/Debug.java
@@ -34,6 +34,7 @@
 import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.HashMap;
 import java.util.Map;
 
 import org.apache.harmony.dalvik.ddmc.Chunk;
@@ -389,6 +390,132 @@
             }
         }
 
+      /**
+       * Returns the value of a particular memory statistic or {@code null} if no
+       * such memory statistic exists.
+       *
+       * <p>The following table lists the memory statistics that are supported.
+       * Note that memory statistics may be added or removed in a future API level.</p>
+       *
+       * <table>
+       *     <thead>
+       *         <tr>
+       *             <th>Memory statistic name</th>
+       *             <th>Meaning</th>
+       *             <th>Example</th>
+       *             <th>Supported (API Levels)</th>
+       *         </tr>
+       *     </thead>
+       *     <tbody>
+       *         <tr>
+       *             <td>summary.java-heap</td>
+       *             <td>The private Java Heap usage in kB. This corresponds to the Java Heap field
+       *                 in the App Summary section output by dumpsys meminfo.</td>
+       *             <td>{@code 1442}</td>
+       *             <td>23</td>
+       *         </tr>
+       *         <tr>
+       *             <td>summary.native-heap</td>
+       *             <td>The private Native Heap usage in kB. This corresponds to the Native Heap
+       *                 field in the App Summary section output by dumpsys meminfo.</td>
+       *             <td>{@code 1442}</td>
+       *             <td>23</td>
+       *         </tr>
+       *         <tr>
+       *             <td>summary.code</td>
+       *             <td>The memory usage for static code and resources in kB. This corresponds to
+       *                 the Code field in the App Summary section output by dumpsys meminfo.</td>
+       *             <td>{@code 1442}</td>
+       *             <td>23</td>
+       *         </tr>
+       *         <tr>
+       *             <td>summary.stack</td>
+       *             <td>The stack usage in kB. This corresponds to the Stack field in the
+       *                 App Summary section output by dumpsys meminfo.</td>
+       *             <td>{@code 1442}</td>
+       *             <td>23</td>
+       *         </tr>
+       *         <tr>
+       *             <td>summary.graphics</td>
+       *             <td>The graphics usage in kB. This corresponds to the Graphics field in the
+       *                 App Summary section output by dumpsys meminfo.</td>
+       *             <td>{@code 1442}</td>
+       *             <td>23</td>
+       *         </tr>
+       *         <tr>
+       *             <td>summary.private-other</td>
+       *             <td>Other private memory usage in kB. This corresponds to the Private Other
+       *                 field output in the App Summary section by dumpsys meminfo.</td>
+       *             <td>{@code 1442}</td>
+       *             <td>23</td>
+       *         </tr>
+       *         <tr>
+       *             <td>summary.system</td>
+       *             <td>Shared and system memory usage in kB. This corresponds to the System
+       *                 field output in the App Summary section by dumpsys meminfo.</td>
+       *             <td>{@code 1442}</td>
+       *             <td>23</td>
+       *         </tr>
+       *         <tr>
+       *             <td>summary.total-pss</td>
+       *             <td>Total PPS memory usage in kB.</td>
+       *             <td>{@code 1442}</td>
+       *             <td>23</td>
+       *         </tr>
+       *         <tr>
+       *             <td>summary.total-swap</td>
+       *             <td>Total swap usage in kB.</td>
+       *             <td>{@code 1442}</td>
+       *             <td>23</td>
+       *         </tr>
+       *     </tbody>
+       * </table>
+       */
+        public String getMemoryStat(String statName) {
+            switch(statName) {
+                case "summary.java-heap":
+                    return Integer.toString(getSummaryJavaHeap());
+                case "summary.native-heap":
+                    return Integer.toString(getSummaryNativeHeap());
+                case "summary.code":
+                    return Integer.toString(getSummaryCode());
+                case "summary.stack":
+                    return Integer.toString(getSummaryStack());
+                case "summary.graphics":
+                    return Integer.toString(getSummaryGraphics());
+                case "summary.private-other":
+                    return Integer.toString(getSummaryPrivateOther());
+                case "summary.system":
+                    return Integer.toString(getSummarySystem());
+                case "summary.total-pss":
+                    return Integer.toString(getSummaryTotalPss());
+                case "summary.total-swap":
+                    return Integer.toString(getSummaryTotalSwap());
+                default:
+                    return null;
+            }
+        }
+
+        /**
+         * Returns a map of the names/values of the memory statistics
+         * that {@link #getMemoryStat(String)} supports.
+         *
+         * @return a map of the names/values of the supported memory statistics.
+         */
+        public Map<String, String> getMemoryStats() {
+            Map<String, String> stats = new HashMap<String, String>();
+            stats.put("summary.java-heap", Integer.toString(getSummaryJavaHeap()));
+            stats.put("summary.native-heap", Integer.toString(getSummaryNativeHeap()));
+            stats.put("summary.code", Integer.toString(getSummaryCode()));
+            stats.put("summary.stack", Integer.toString(getSummaryStack()));
+            stats.put("summary.graphics", Integer.toString(getSummaryGraphics()));
+            stats.put("summary.private-other", Integer.toString(getSummaryPrivateOther()));
+            stats.put("summary.system", Integer.toString(getSummarySystem()));
+            stats.put("summary.total-pss", Integer.toString(getSummaryTotalPss()));
+            stats.put("summary.total-swap", Integer.toString(getSummaryTotalSwap()));
+            return stats;
+        }
+
         /**
          * Pss of Java Heap bytes in KB due to the application.
          * Notes:
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 009649f..dbb5146 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -21,6 +21,7 @@
 import android.system.Os;
 import android.util.Log;
 import com.android.internal.os.Zygote;
+import dalvik.system.VMRuntime;
 import java.io.BufferedWriter;
 import java.io.DataInputStream;
 import java.io.IOException;
@@ -744,7 +745,14 @@
      * @return  Returns the number of milliseconds this process has return.
      */
     public static final native long getElapsedCpuTime();
-    
+
+    /**
+     * Returns true if the current process is a 64-bit runtime.
+     */
+    public static final boolean is64Bit() {
+        return VMRuntime.getRuntime().is64Bit();
+    }
+
     /**
      * Returns the identifier of this process, which can be used with
      * {@link #killProcess} and {@link #sendSignal}.
diff --git a/core/java/android/util/LayoutDirection.java b/core/java/android/util/LayoutDirection.java
index 20af20b..03077e4 100644
--- a/core/java/android/util/LayoutDirection.java
+++ b/core/java/android/util/LayoutDirection.java
@@ -27,6 +27,12 @@
     private LayoutDirection() {}
 
     /**
+     * An undefined layout direction.
+     * @hide
+     */
+    public static final int UNDEFINED = -1;
+
+    /**
      * Horizontal layout direction is from Left to Right.
      */
     public static final int LTR = 0;
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 342315b..5dd5ab8 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -1872,6 +1872,12 @@
     public @interface ResolvedLayoutDir {}
 
     /**
+     * A flag to indicate that the layout direction of this view has not been defined yet.
+     * @hide
+     */
+    public static final int LAYOUT_DIRECTION_UNDEFINED = LayoutDirection.UNDEFINED;
+
+    /**
      * Horizontal layout direction of this view is from Left to Right.
      * Use with {@link #setLayoutDirection}.
      */
diff --git a/core/java/android/view/inputmethod/EditorInfo.java b/core/java/android/view/inputmethod/EditorInfo.java
index c0395cf..687f9d0 100644
--- a/core/java/android/view/inputmethod/EditorInfo.java
+++ b/core/java/android/view/inputmethod/EditorInfo.java
@@ -298,6 +298,19 @@
 
     /**
      * Name of the package that owns this editor.
+     *
+     * <p><strong>IME authors:</strong> In API level 22
+     * {@link android.os.Build.VERSION_CODES#LOLLIPOP_MR1} and prior, do not trust this package
+     * name. The system had not verified the consistency between the package name here and
+     * application's uid. Consider to use {@link InputBinding#getUid()}, which is trustworthy.
+     * Starting from Android MNC, the system verifies the consistency between this package name
+     * and application uid before {@link EditorInfo} is passed to the input method as long as the
+     * application sets a non-empty package name.</p>
+     *
+     * <p><strong>Editor authors:</strong> Starting from Android MNC, the application is no longer
+     * able to establish input connections if the package name provided here is inconsistent with
+     * application's uid. Note that the system does accept an empty string for an arbitrary
+     * application uid.</p>
      */
     public String packageName;
 
diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java
index f153ce5..9d14254 100644
--- a/core/java/android/widget/LinearLayout.java
+++ b/core/java/android/widget/LinearLayout.java
@@ -185,6 +185,8 @@
     private int mShowDividers;
     private int mDividerPadding;
 
+    private int mLayoutDirection = View.LAYOUT_DIRECTION_UNDEFINED;
+
     public LinearLayout(Context context) {
         this(context, null);
     }
@@ -1567,6 +1569,17 @@
         }
     }
 
+    @Override
+    public void onRtlPropertiesChanged(@ResolvedLayoutDir int layoutDirection) {
+        super.onRtlPropertiesChanged(layoutDirection);
+        if (layoutDirection != mLayoutDirection) {
+            mLayoutDirection = layoutDirection;
+            if (mOrientation == HORIZONTAL) {
+                requestLayout();
+            }
+        }
+    }
+
     /**
      * Position the children during a layout pass if the orientation of this
      * LinearLayout is set to {@link #HORIZONTAL}.
diff --git a/core/java/com/android/internal/os/BatteryStatsHelper.java b/core/java/com/android/internal/os/BatteryStatsHelper.java
index e6165a1..4290e22 100644
--- a/core/java/com/android/internal/os/BatteryStatsHelper.java
+++ b/core/java/com/android/internal/os/BatteryStatsHelper.java
@@ -354,9 +354,9 @@
 
         if (mBluetoothPowerCalculator == null) {
             if (checkHasBluetoothPowerReporting(mStats, mPowerProfile)) {
-                mBluetoothPowerCalculator = new BluetoothPowerCalculator();
+                mBluetoothPowerCalculator = new BluetoothPowerCalculator(mPowerProfile);
             } else {
-                mBluetoothPowerCalculator = new BluetoothPowerCalculator();
+                mBluetoothPowerCalculator = new BluetoothPowerCalculator(mPowerProfile);
             }
         }
         mBluetoothPowerCalculator.reset();
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index ee7ed0b..087db78 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -7814,11 +7814,13 @@
             mWifiActivityCounters[CONTROLLER_IDLE_TIME].addCountLocked(
                     info.getControllerIdleTimeMillis());
 
-            final double opVoltage = mPowerProfile.getAveragePower(
-                    PowerProfile.POWER_WIFI_CONTROLLER_OPERATING_VOLTAGE);
-            if (opVoltage != 0) {
+            // POWER_WIFI_CONTROLLER_OPERATING_VOLTAGE is measured in mV, so convert to V.
+            final double opVolt = mPowerProfile.getAveragePower(
+                    PowerProfile.POWER_WIFI_CONTROLLER_OPERATING_VOLTAGE) / 1000.0;
+            if (opVolt != 0) {
+                // We store the power drain as mAms.
                 mWifiActivityCounters[CONTROLLER_POWER_DRAIN].addCountLocked(
-                        (long)(info.getControllerEnergyUsed() / opVoltage));
+                        (long)(info.getControllerEnergyUsed() / opVolt));
             }
         }
     }
@@ -7908,11 +7910,13 @@
             mBluetoothActivityCounters[CONTROLLER_IDLE_TIME].addCountLocked(
                     info.getControllerIdleTimeMillis());
 
-            final double opVoltage = mPowerProfile.getAveragePower(
-                    PowerProfile.POWER_BLUETOOTH_CONTROLLER_OPERATING_VOLTAGE);
-            if (opVoltage != 0) {
+            // POWER_BLUETOOTH_CONTROLLER_OPERATING_VOLTAGE is measured in mV, so convert to V.
+            final double opVolt = mPowerProfile.getAveragePower(
+                    PowerProfile.POWER_BLUETOOTH_CONTROLLER_OPERATING_VOLTAGE) / 1000.0;
+            if (opVolt != 0) {
+                // We store the power drain as mAms.
                 mBluetoothActivityCounters[CONTROLLER_POWER_DRAIN].addCountLocked(
-                        (long) (info.getControllerEnergyUsed() / opVoltage));
+                        (long) (info.getControllerEnergyUsed() / opVolt));
             }
         }
     }
diff --git a/core/java/com/android/internal/os/BluetoothPowerCalculator.java b/core/java/com/android/internal/os/BluetoothPowerCalculator.java
index 3557209..1f59672 100644
--- a/core/java/com/android/internal/os/BluetoothPowerCalculator.java
+++ b/core/java/com/android/internal/os/BluetoothPowerCalculator.java
@@ -21,6 +21,15 @@
 public class BluetoothPowerCalculator extends PowerCalculator {
     private static final boolean DEBUG = BatteryStatsHelper.DEBUG;
     private static final String TAG = "BluetoothPowerCalculator";
+    private final double mIdleMa;
+    private final double mRxMa;
+    private final double mTxMa;
+
+    public BluetoothPowerCalculator(PowerProfile profile) {
+        mIdleMa = profile.getAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_IDLE);
+        mRxMa = profile.getAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_RX);
+        mTxMa = profile.getAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_TX);
+    }
 
     @Override
     public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs,
@@ -37,10 +46,15 @@
                 BatteryStats.CONTROLLER_TX_TIME, statsType);
         final long rxTimeMs = stats.getBluetoothControllerActivity(
                 BatteryStats.CONTROLLER_RX_TIME, statsType);
-        final long powerMaMs = stats.getBluetoothControllerActivity(
-                BatteryStats.CONTROLLER_POWER_DRAIN, statsType);
-        final double powerMah = powerMaMs / (double)(1000*60*60);
         final long totalTimeMs = idleTimeMs + txTimeMs + rxTimeMs;
+        double powerMah = stats.getBluetoothControllerActivity(
+                BatteryStats.CONTROLLER_POWER_DRAIN, statsType) / (double)(1000*60*60);
+
+        if (powerMah == 0) {
+            // Some devices do not report the power, so calculate it.
+            powerMah = ((idleTimeMs * mIdleMa) + (rxTimeMs * mRxMa) + (txTimeMs * mTxMa))
+                    / (1000*60*60);
+        }
 
         if (DEBUG && powerMah != 0) {
             Log.d(TAG, "Bluetooth active: time=" + (totalTimeMs)
diff --git a/core/java/com/android/internal/os/WifiPowerCalculator.java b/core/java/com/android/internal/os/WifiPowerCalculator.java
index 961b0df..da98a67 100644
--- a/core/java/com/android/internal/os/WifiPowerCalculator.java
+++ b/core/java/com/android/internal/os/WifiPowerCalculator.java
@@ -70,14 +70,14 @@
                 statsType);
         app.wifiRunningTimeMs = idleTimeMs + rxTimeMs + txTimeMs;
 
-        double powerDrain = stats.getWifiControllerActivity(BatteryStats.CONTROLLER_POWER_DRAIN,
+        double powerDrainMah = stats.getWifiControllerActivity(BatteryStats.CONTROLLER_POWER_DRAIN,
                 statsType) / (double)(1000*60*60);
-        if (powerDrain == 0) {
+        if (powerDrainMah == 0) {
             // Some controllers do not report power drain, so we can calculate it here.
-            powerDrain = ((idleTimeMs * mIdleCurrentMa) + (txTimeMs * mTxCurrentMa)
+            powerDrainMah = ((idleTimeMs * mIdleCurrentMa) + (txTimeMs * mTxCurrentMa)
                     + (rxTimeMs * mRxCurrentMa)) / (1000*60*60);
         }
-        app.wifiPowerMah = Math.max(0, powerDrain - mTotalAppPowerDrain);
+        app.wifiPowerMah = Math.max(0, powerDrainMah - mTotalAppPowerDrain);
 
         if (DEBUG) {
             Log.d(TAG, "left over WiFi power: " + BatteryStatsHelper.makemAh(app.wifiPowerMah));
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index c010dfe..294e4ba 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -2741,7 +2741,14 @@
                 try {
                     mode = getCallback().onWindowStartingActionMode(wrappedCallback, type);
                 } catch (AbstractMethodError ame) {
-                    // Older apps might not implement this callback method.
+                    // Older apps might not implement the typed version of this method.
+                    if (type == ActionMode.TYPE_PRIMARY) {
+                        try {
+                            mode = getCallback().onWindowStartingActionMode(wrappedCallback);
+                        } catch (AbstractMethodError ame2) {
+                            // Older apps might not implement this callback method at all.
+                        }
+                    }
                 }
             }
             if (mode != null) {
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 3f828e7..baa7a35 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -414,8 +414,14 @@
     <string name="silent_mode_ring">Ringer on</string>
 
     <!-- Reboot to Recovery Progress Dialog. This is shown before it reboots to recovery. -->
-    <string name="reboot_to_recovery_title">Prepare for update</string>
-    <string name="reboot_to_recovery_progress">Processing the update package\u2026</string>
+    <string name="reboot_to_update_title">Android system update</string>
+    <string name="reboot_to_update_prepare">Preparing to update\u2026</string>
+    <string name="reboot_to_update_package">Processing the update package\u2026</string>
+    <string name="reboot_to_update_reboot">Restarting\u2026</string>
+
+    <!-- Reboot to Recovery for factory reset. -->
+    <string name="reboot_to_reset_title">Factory data reset</string>
+    <string name="reboot_to_reset_message">Restarting\u2026</string>
 
     <!-- Shutdown Progress Dialog. This is shown if the user chooses to power off the phone. -->
     <string name="shutdown_progress">Shutting down\u2026</string>
@@ -542,7 +548,7 @@
     <!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permgrouplab_location">Location</string>
     <!-- Description of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permgroupdesc_location">access your location</string>
+    <string name="permgroupdesc_location">access this device\'s location</string>
 
     <!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permgrouplab_socialInfo">Your social information</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index c7f27e1..c765563 100755
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -820,8 +820,12 @@
   <java-symbol type="string" name="mobile_provisioning_url" />
   <java-symbol type="string" name="mobile_redirected_provisioning_url" />
   <java-symbol type="string" name="quick_contacts_not_available" />
-  <java-symbol type="string" name="reboot_to_recovery_progress" />
-  <java-symbol type="string" name="reboot_to_recovery_title" />
+  <java-symbol type="string" name="reboot_to_update_package" />
+  <java-symbol type="string" name="reboot_to_update_prepare" />
+  <java-symbol type="string" name="reboot_to_update_title" />
+  <java-symbol type="string" name="reboot_to_update_reboot" />
+  <java-symbol type="string" name="reboot_to_reset_title" />
+  <java-symbol type="string" name="reboot_to_reset_message" />
   <java-symbol type="string" name="reboot_safemode_confirm" />
   <java-symbol type="string" name="reboot_safemode_title" />
   <java-symbol type="string" name="relationTypeAssistant" />
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java b/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java
index 0e8d03e..19375a2 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java
@@ -31,11 +31,18 @@
 import java.security.GeneralSecurityException;
 import java.security.InvalidAlgorithmParameterException;
 import java.security.InvalidKeyException;
+import java.security.InvalidParameterException;
 import java.security.Key;
+import java.security.KeyFactory;
 import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
 import java.security.ProviderException;
+import java.security.PublicKey;
 import java.security.SecureRandom;
 import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
 
 import javax.crypto.AEADBadTagException;
 import javax.crypto.BadPaddingException;
@@ -43,7 +50,10 @@
 import javax.crypto.CipherSpi;
 import javax.crypto.IllegalBlockSizeException;
 import javax.crypto.NoSuchPaddingException;
+import javax.crypto.SecretKey;
+import javax.crypto.SecretKeyFactory;
 import javax.crypto.ShortBufferException;
+import javax.crypto.spec.SecretKeySpec;
 
 /**
  * Base class for {@link CipherSpi} implementations of Android KeyStore backed ciphers.
@@ -140,11 +150,18 @@
     }
 
     private void init(int opmode, Key key, SecureRandom random) throws InvalidKeyException {
-        if ((opmode != Cipher.ENCRYPT_MODE) && (opmode != Cipher.DECRYPT_MODE)) {
-            throw new UnsupportedOperationException(
-                    "Only ENCRYPT and DECRYPT modes supported. Mode: " + opmode);
+        switch (opmode) {
+            case Cipher.ENCRYPT_MODE:
+            case Cipher.WRAP_MODE:
+                mEncrypting = true;
+                break;
+            case Cipher.DECRYPT_MODE:
+            case Cipher.UNWRAP_MODE:
+                mEncrypting = false;
+                break;
+            default:
+                throw new InvalidParameterException("Unsupported opmode: " + opmode);
         }
-        mEncrypting = opmode == Cipher.ENCRYPT_MODE;
         initKey(opmode, key);
         if (mKey == null) {
             throw new ProviderException("initKey did not initialize the key");
@@ -395,13 +412,139 @@
     @Override
     protected final byte[] engineWrap(Key key)
             throws IllegalBlockSizeException, InvalidKeyException {
-        return super.engineWrap(key);
+        if (mKey == null) {
+            throw new IllegalStateException("Not initilized");
+        }
+
+        if (!isEncrypting()) {
+            throw new IllegalStateException(
+                    "Cipher must be initialized in Cipher.WRAP_MODE to wrap keys");
+        }
+
+        if (key == null) {
+            throw new NullPointerException("key == null");
+        }
+        byte[] encoded = null;
+        if (key instanceof SecretKey) {
+            if ("RAW".equalsIgnoreCase(key.getFormat())) {
+                encoded = key.getEncoded();
+            }
+            if (encoded == null) {
+                try {
+                    SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(key.getAlgorithm());
+                    SecretKeySpec spec =
+                            (SecretKeySpec) keyFactory.getKeySpec(
+                                    (SecretKey) key, SecretKeySpec.class);
+                    encoded = spec.getEncoded();
+                } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
+                    throw new InvalidKeyException(
+                            "Failed to wrap key because it does not export its key material",
+                            e);
+                }
+            }
+        } else if (key instanceof PrivateKey) {
+            if ("PKCS8".equalsIgnoreCase(key.getFormat())) {
+                encoded = key.getEncoded();
+            }
+            if (encoded == null) {
+                try {
+                    KeyFactory keyFactory = KeyFactory.getInstance(key.getAlgorithm());
+                    PKCS8EncodedKeySpec spec =
+                            keyFactory.getKeySpec(key, PKCS8EncodedKeySpec.class);
+                    encoded = spec.getEncoded();
+                } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
+                    throw new InvalidKeyException(
+                            "Failed to wrap key because it does not export its key material",
+                            e);
+                }
+            }
+        } else if (key instanceof PublicKey) {
+            if ("X.509".equalsIgnoreCase(key.getFormat())) {
+                encoded = key.getEncoded();
+            }
+            if (encoded == null) {
+                try {
+                    KeyFactory keyFactory = KeyFactory.getInstance(key.getAlgorithm());
+                    X509EncodedKeySpec spec =
+                            keyFactory.getKeySpec(key, X509EncodedKeySpec.class);
+                    encoded = spec.getEncoded();
+                } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
+                    throw new InvalidKeyException(
+                            "Failed to wrap key because it does not export its key material",
+                            e);
+                }
+            }
+        } else {
+            throw new InvalidKeyException("Unsupported key type: " + key.getClass().getName());
+        }
+
+        if (encoded == null) {
+            throw new InvalidKeyException(
+                    "Failed to wrap key because it does not export its key material");
+        }
+
+        try {
+            return engineDoFinal(encoded, 0, encoded.length);
+        } catch (BadPaddingException e) {
+            throw (IllegalBlockSizeException) new IllegalBlockSizeException().initCause(e);
+        }
     }
 
     @Override
     protected final Key engineUnwrap(byte[] wrappedKey, String wrappedKeyAlgorithm,
             int wrappedKeyType) throws InvalidKeyException, NoSuchAlgorithmException {
-        return super.engineUnwrap(wrappedKey, wrappedKeyAlgorithm, wrappedKeyType);
+        if (mKey == null) {
+            throw new IllegalStateException("Not initilized");
+        }
+
+        if (isEncrypting()) {
+            throw new IllegalStateException(
+                    "Cipher must be initialized in Cipher.WRAP_MODE to wrap keys");
+        }
+
+        if (wrappedKey == null) {
+            throw new NullPointerException("wrappedKey == null");
+        }
+
+        byte[] encoded;
+        try {
+            encoded = engineDoFinal(wrappedKey, 0, wrappedKey.length);
+        } catch (IllegalBlockSizeException | BadPaddingException e) {
+            throw new InvalidKeyException("Failed to unwrap key", e);
+        }
+
+        switch (wrappedKeyType) {
+            case Cipher.SECRET_KEY:
+            {
+                return new SecretKeySpec(encoded, wrappedKeyAlgorithm);
+                // break;
+            }
+            case Cipher.PRIVATE_KEY:
+            {
+                KeyFactory keyFactory = KeyFactory.getInstance(wrappedKeyAlgorithm);
+                try {
+                    return keyFactory.generatePrivate(new PKCS8EncodedKeySpec(encoded));
+                } catch (InvalidKeySpecException e) {
+                    throw new InvalidKeyException(
+                            "Failed to create private key from its PKCS#8 encoded form", e);
+                }
+                // break;
+            }
+            case Cipher.PUBLIC_KEY:
+            {
+                KeyFactory keyFactory = KeyFactory.getInstance(wrappedKeyAlgorithm);
+                try {
+                    return keyFactory.generatePublic(new X509EncodedKeySpec(encoded));
+                } catch (InvalidKeySpecException e) {
+                    throw new InvalidKeyException(
+                            "Failed to create public key from its X.509 encoded form", e);
+                }
+                // break;
+            }
+            default:
+                throw new InvalidParameterException(
+                        "Unsupported wrappedKeyType: " + wrappedKeyType);
+        }
     }
 
     @Override
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreRSACipherSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreRSACipherSpi.java
index 5643caf..d33692a 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreRSACipherSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreRSACipherSpi.java
@@ -453,7 +453,7 @@
                 case Cipher.ENCRYPT_MODE:
                 case Cipher.WRAP_MODE:
                     // Permitted
-                    return;
+                    break;
                 case Cipher.DECRYPT_MODE:
                 case Cipher.UNWRAP_MODE:
                     throw new InvalidKeyException("RSA public keys cannot be used with opmode: "
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index 7eb1357..3cbc405 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -1277,7 +1277,8 @@
                     native_enableDeviceCallback();
                 }
                 mRoutingChangeListeners.put(
-                    listener, new NativeRoutingEventHandlerDelegate(this, listener, handler));
+                    listener, new NativeRoutingEventHandlerDelegate(this, listener,
+                            handler != null ? handler : new Handler(mInitializationLooper)));
             }
         }
     }
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index 7293c6c..f395cb3 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -2244,7 +2244,8 @@
                     native_enableDeviceCallback();
                 }
                 mRoutingChangeListeners.put(
-                    listener, new NativeRoutingEventHandlerDelegate(this, listener, handler));
+                    listener, new NativeRoutingEventHandlerDelegate(this, listener,
+                            handler != null ? handler : new Handler(mInitializationLooper)));
             }
         }
     }
diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java
index aa5d43a..6bf5721 100644
--- a/media/java/android/media/ExifInterface.java
+++ b/media/java/android/media/ExifInterface.java
@@ -57,6 +57,16 @@
     public static final String TAG_APERTURE = "FNumber";
     /** Type is String. */
     public static final String TAG_ISO = "ISOSpeedRatings";
+    /** Type is String. */
+    public static final String TAG_DATETIME_DIGITIZED = "DateTimeDigitized";
+    /** Type is int. */
+    public static final String TAG_SUBSEC_TIME = "SubSecTime";
+    /** Type is int. */
+    public static final String TAG_SUBSEC_TIME_ORIG = "SubSecTimeOriginal";
+    /** Type is int. */
+    public static final String TAG_SUBSEC_TIME_DIG = "SubSecTimeDigitized";
+
+
 
     /**
      * @hide
diff --git a/media/jni/audioeffect/android_media_AudioEffect.cpp b/media/jni/audioeffect/android_media_AudioEffect.cpp
index 96b72a2..fdc586b 100644
--- a/media/jni/audioeffect/android_media_AudioEffect.cpp
+++ b/media/jni/audioeffect/android_media_AudioEffect.cpp
@@ -92,6 +92,7 @@
     }
 }
 
+static Mutex sLock;
 
 // ----------------------------------------------------------------------------
 static void effectCallback(int event, void* user, void *info) {
@@ -182,6 +183,32 @@
 }
 
 // ----------------------------------------------------------------------------
+
+static sp<AudioEffect> getAudioEffect(JNIEnv* env, jobject thiz)
+{
+    Mutex::Autolock l(sLock);
+    AudioEffect* const ae =
+            (AudioEffect*)env->GetLongField(thiz, fields.fidNativeAudioEffect);
+    return sp<AudioEffect>(ae);
+}
+
+static sp<AudioEffect> setAudioEffect(JNIEnv* env, jobject thiz,
+                                    const sp<AudioEffect>& ae)
+{
+    Mutex::Autolock l(sLock);
+    sp<AudioEffect> old =
+            (AudioEffect*)env->GetLongField(thiz, fields.fidNativeAudioEffect);
+    if (ae.get()) {
+        ae->incStrong((void*)setAudioEffect);
+    }
+    if (old != 0) {
+        old->decStrong((void*)setAudioEffect);
+    }
+    env->SetLongField(thiz, fields.fidNativeAudioEffect, (jlong)ae.get());
+    return old;
+}
+
+// ----------------------------------------------------------------------------
 // This function gets some field IDs, which in turn causes class initialization.
 // It is called from a static block in AudioEffect, which won't run until the
 // first time an instance of this class is used.
@@ -257,7 +284,7 @@
     ALOGV("android_media_AudioEffect_native_setup");
     AudioEffectJniStorage* lpJniStorage = NULL;
     int lStatus = AUDIOEFFECT_ERROR_NO_MEMORY;
-    AudioEffect* lpAudioEffect = NULL;
+    sp<AudioEffect> lpAudioEffect;
     jint* nId = NULL;
     const char *typeStr = NULL;
     const char *uuidStr = NULL;
@@ -272,6 +299,8 @@
 
     ScopedUtfChars opPackageNameStr(env, opPackageName);
 
+    setAudioEffect(env, thiz, 0);
+
     if (type != NULL) {
         typeStr = env->GetStringUTFChars(type, NULL);
         if (typeStr == NULL) {  // Out of memory
@@ -324,7 +353,7 @@
                                     &lpJniStorage->mCallbackData,
                                     sessionId,
                                     0);
-    if (lpAudioEffect == NULL) {
+    if (lpAudioEffect == 0) {
         ALOGE("Error creating AudioEffect");
         goto setup_failure;
     }
@@ -394,7 +423,7 @@
 
     env->SetObjectArrayElement(javadesc, 0, jdesc);
 
-    env->SetLongField(thiz, fields.fidNativeAudioEffect, (jlong)lpAudioEffect);
+    setAudioEffect(env, thiz, lpAudioEffect);
 
     env->SetLongField(thiz, fields.fidJniData, (jlong)lpJniStorage);
 
@@ -407,12 +436,9 @@
         env->ReleasePrimitiveArrayCritical(jId, nId, 0);
     }
 
-    if (lpAudioEffect) {
-        delete lpAudioEffect;
-    }
-    env->SetLongField(thiz, fields.fidNativeAudioEffect, 0);
-
     if (lpJniStorage) {
+        env->DeleteGlobalRef(lpJniStorage->mCallbackData.audioEffect_class);
+        env->DeleteGlobalRef(lpJniStorage->mCallbackData.audioEffect_ref);
         delete lpJniStorage;
     }
     env->SetLongField(thiz, fields.fidJniData, 0);
@@ -430,20 +456,20 @@
 
 
 // ----------------------------------------------------------------------------
-static void android_media_AudioEffect_native_finalize(JNIEnv *env,  jobject thiz) {
-    ALOGV("android_media_AudioEffect_native_finalize jobject: %p\n", thiz);
-
-    // delete the AudioEffect object
-    AudioEffect* lpAudioEffect = (AudioEffect *)env->GetLongField(
-        thiz, fields.fidNativeAudioEffect);
-    if (lpAudioEffect) {
-        ALOGV("deleting AudioEffect: %p\n", lpAudioEffect);
-        delete lpAudioEffect;
+static void android_media_AudioEffect_native_release(JNIEnv *env,  jobject thiz) {
+    sp<AudioEffect> lpAudioEffect = setAudioEffect(env, thiz, 0);
+    if (lpAudioEffect == 0) {
+        return;
     }
 
     // delete the JNI data
-    AudioEffectJniStorage* lpJniStorage = (AudioEffectJniStorage *)env->GetLongField(
-        thiz, fields.fidJniData);
+    AudioEffectJniStorage* lpJniStorage =
+        (AudioEffectJniStorage *)env->GetLongField(thiz, fields.fidJniData);
+
+    // reset the native resources in the Java object so any attempt to access
+    // them after a call to release fails.
+    env->SetLongField(thiz, fields.fidJniData, 0);
+
     if (lpJniStorage) {
         ALOGV("deleting pJniStorage: %p\n", lpJniStorage);
         delete lpJniStorage;
@@ -451,24 +477,16 @@
 }
 
 // ----------------------------------------------------------------------------
-static void android_media_AudioEffect_native_release(JNIEnv *env,  jobject thiz) {
-
-    // do everything a call to finalize would
-    android_media_AudioEffect_native_finalize(env, thiz);
-    // + reset the native resources in the Java object so any attempt to access
-    // them after a call to release fails.
-    env->SetLongField(thiz, fields.fidNativeAudioEffect, 0);
-    env->SetLongField(thiz, fields.fidJniData, 0);
+static void android_media_AudioEffect_native_finalize(JNIEnv *env,  jobject thiz) {
+    ALOGV("android_media_AudioEffect_native_finalize jobject: %p\n", thiz);
+    android_media_AudioEffect_native_release(env, thiz);
 }
 
 static jint
 android_media_AudioEffect_native_setEnabled(JNIEnv *env, jobject thiz, jboolean enabled)
 {
-    // retrieve the AudioEffect object
-    AudioEffect* lpAudioEffect = (AudioEffect *)env->GetLongField(
-        thiz, fields.fidNativeAudioEffect);
-
-    if (lpAudioEffect == NULL) {
+    sp<AudioEffect> lpAudioEffect = getAudioEffect(env, thiz);
+    if (lpAudioEffect == 0) {
         jniThrowException(env, "java/lang/IllegalStateException",
             "Unable to retrieve AudioEffect pointer for enable()");
         return AUDIOEFFECT_ERROR_NO_INIT;
@@ -480,11 +498,8 @@
 static jboolean
 android_media_AudioEffect_native_getEnabled(JNIEnv *env, jobject thiz)
 {
-    // retrieve the AudioEffect object
-    AudioEffect* lpAudioEffect = (AudioEffect *)env->GetLongField(
-        thiz, fields.fidNativeAudioEffect);
-
-    if (lpAudioEffect == NULL) {
+  sp<AudioEffect> lpAudioEffect = getAudioEffect(env, thiz);
+  if (lpAudioEffect == 0) {
         jniThrowException(env, "java/lang/IllegalStateException",
             "Unable to retrieve AudioEffect pointer for getEnabled()");
         return JNI_FALSE;
@@ -501,11 +516,8 @@
 static jboolean
 android_media_AudioEffect_native_hasControl(JNIEnv *env, jobject thiz)
 {
-    // retrieve the AudioEffect object
-    AudioEffect* lpAudioEffect = (AudioEffect *)env->GetLongField(
-        thiz, fields.fidNativeAudioEffect);
-
-    if (lpAudioEffect == NULL) {
+  sp<AudioEffect> lpAudioEffect = getAudioEffect(env, thiz);
+  if (lpAudioEffect == 0) {
         jniThrowException(env, "java/lang/IllegalStateException",
             "Unable to retrieve AudioEffect pointer for hasControl()");
         return JNI_FALSE;
@@ -528,10 +540,8 @@
     effect_param_t *p;
     int voffset;
 
-    AudioEffect* lpAudioEffect = (AudioEffect *) env->GetLongField(thiz,
-            fields.fidNativeAudioEffect);
-
-    if (lpAudioEffect == NULL) {
+    sp<AudioEffect> lpAudioEffect = getAudioEffect(env, thiz);
+    if (lpAudioEffect == 0) {
         jniThrowException(env, "java/lang/IllegalStateException",
                 "Unable to retrieve AudioEffect pointer for setParameter()");
         return AUDIOEFFECT_ERROR_NO_INIT;
@@ -591,10 +601,8 @@
     effect_param_t *p;
     int voffset;
 
-    AudioEffect* lpAudioEffect = (AudioEffect *) env->GetLongField(thiz,
-            fields.fidNativeAudioEffect);
-
-    if (lpAudioEffect == NULL) {
+    sp<AudioEffect> lpAudioEffect = getAudioEffect(env, thiz);
+    if (lpAudioEffect == 0) {
         jniThrowException(env, "java/lang/IllegalStateException",
                 "Unable to retrieve AudioEffect pointer for getParameter()");
         return AUDIOEFFECT_ERROR_NO_INIT;
@@ -657,11 +665,8 @@
     jbyte* pReplyData = NULL;
     jint lStatus = AUDIOEFFECT_ERROR_BAD_VALUE;
 
-    // retrieve the AudioEffect object
-    AudioEffect* lpAudioEffect = (AudioEffect *) env->GetLongField(thiz,
-            fields.fidNativeAudioEffect);
-
-    if (lpAudioEffect == NULL) {
+    sp<AudioEffect> lpAudioEffect = getAudioEffect(env, thiz);
+    if (lpAudioEffect == 0) {
         jniThrowException(env, "java/lang/IllegalStateException",
                 "Unable to retrieve AudioEffect pointer for setParameter()");
         return AUDIOEFFECT_ERROR_NO_INIT;
diff --git a/media/jni/audioeffect/android_media_Visualizer.cpp b/media/jni/audioeffect/android_media_Visualizer.cpp
index abc681e..6098b4a 100644
--- a/media/jni/audioeffect/android_media_Visualizer.cpp
+++ b/media/jni/audioeffect/android_media_Visualizer.cpp
@@ -102,14 +102,14 @@
  };
 
 // ----------------------------------------------------------------------------
-class visualizerJniStorage {
+class VisualizerJniStorage {
     public:
         visualizer_callback_cookie mCallbackData;
 
-    visualizerJniStorage() {
+    VisualizerJniStorage() {
     }
 
-    ~visualizerJniStorage() {
+    ~VisualizerJniStorage() {
     }
 };
 
@@ -135,6 +135,7 @@
     }
 }
 
+static Mutex sLock;
 
 // ----------------------------------------------------------------------------
 static void ensureArraySize(JNIEnv *env, jbyteArray *array, uint32_t size) {
@@ -227,15 +228,30 @@
     }
 }
 
-static Visualizer *getVisualizer(JNIEnv* env, jobject thiz)
+// ----------------------------------------------------------------------------
+
+static sp<Visualizer> getVisualizer(JNIEnv* env, jobject thiz)
 {
-    Visualizer *v = (Visualizer *)env->GetLongField(
-        thiz, fields.fidNativeVisualizer);
-    if (v == NULL) {
-        jniThrowException(env, "java/lang/IllegalStateException",
-            "Unable to retrieve Visualizer pointer");
+    Mutex::Autolock l(sLock);
+    Visualizer* const v =
+            (Visualizer*)env->GetLongField(thiz, fields.fidNativeVisualizer);
+    return sp<Visualizer>(v);
+}
+
+static sp<Visualizer> setVisualizer(JNIEnv* env, jobject thiz,
+                                    const sp<Visualizer>& v)
+{
+    Mutex::Autolock l(sLock);
+    sp<Visualizer> old =
+            (Visualizer*)env->GetLongField(thiz, fields.fidNativeVisualizer);
+    if (v.get()) {
+        v->incStrong((void*)setVisualizer);
     }
-    return v;
+    if (old != 0) {
+        old->decStrong((void*)setVisualizer);
+    }
+    env->SetLongField(thiz, fields.fidNativeVisualizer, (jlong)v.get());
+    return old;
 }
 
 // ----------------------------------------------------------------------------
@@ -318,7 +334,7 @@
                                                      void *info) {
     if ((event == AudioEffect::EVENT_ERROR) &&
         (*((status_t*)info) == DEAD_OBJECT)) {
-        visualizerJniStorage* lpJniStorage = (visualizerJniStorage*)user;
+        VisualizerJniStorage* lpJniStorage = (VisualizerJniStorage*)user;
         visualizer_callback_cookie* callbackInfo = &lpJniStorage->mCallbackData;
         JNIEnv *env = AndroidRuntime::getJNIEnv();
 
@@ -336,14 +352,16 @@
         jint sessionId, jintArray jId, jstring opPackageName)
 {
     ALOGV("android_media_visualizer_native_setup");
-    visualizerJniStorage* lpJniStorage = NULL;
+    VisualizerJniStorage* lpJniStorage = NULL;
     int lStatus = VISUALIZER_ERROR_NO_MEMORY;
-    Visualizer* lpVisualizer = NULL;
+    sp<Visualizer> lpVisualizer;
     jint* nId = NULL;
 
     ScopedUtfChars opPackageNameStr(env, opPackageName);
 
-    lpJniStorage = new visualizerJniStorage();
+    setVisualizer(env, thiz, 0);
+
+    lpJniStorage = new VisualizerJniStorage();
     if (lpJniStorage == NULL) {
         ALOGE("setup: Error creating JNI Storage");
         goto setup_failure;
@@ -371,7 +389,7 @@
                                   android_media_visualizer_effect_callback,
                                   lpJniStorage,
                                   sessionId);
-    if (lpVisualizer == NULL) {
+    if (lpVisualizer == 0) {
         ALOGE("Error creating Visualizer");
         goto setup_failure;
     }
@@ -392,7 +410,7 @@
     env->ReleasePrimitiveArrayCritical(jId, nId, 0);
     nId = NULL;
 
-    env->SetLongField(thiz, fields.fidNativeVisualizer, (jlong)lpVisualizer);
+    setVisualizer(env, thiz, lpVisualizer);
 
     env->SetLongField(thiz, fields.fidJniData, (jlong)lpJniStorage);
 
@@ -405,12 +423,9 @@
         env->ReleasePrimitiveArrayCritical(jId, nId, 0);
     }
 
-    if (lpVisualizer) {
-        delete lpVisualizer;
-    }
-    env->SetLongField(thiz, fields.fidNativeVisualizer, 0);
-
     if (lpJniStorage) {
+        env->DeleteGlobalRef(lpJniStorage->mCallbackData.visualizer_class);
+        env->DeleteGlobalRef(lpJniStorage->mCallbackData.visualizer_ref);
         delete lpJniStorage;
     }
     env->SetLongField(thiz, fields.fidJniData, 0);
@@ -419,49 +434,44 @@
 }
 
 // ----------------------------------------------------------------------------
-static void android_media_visualizer_native_finalize(JNIEnv *env,  jobject thiz) {
-    ALOGV("android_media_visualizer_native_finalize jobject: %p\n", thiz);
-
-    // delete the Visualizer object
-    Visualizer* lpVisualizer = (Visualizer *)env->GetLongField(
-        thiz, fields.fidNativeVisualizer);
-    if (lpVisualizer) {
-        ALOGV("deleting Visualizer: %p\n", lpVisualizer);
-        delete lpVisualizer;
+static void android_media_visualizer_native_release(JNIEnv *env,  jobject thiz) {
+    sp<Visualizer> lpVisualizer = setVisualizer(env, thiz, 0);
+    if (lpVisualizer == 0) {
+        return;
     }
 
     // delete the JNI data
-    visualizerJniStorage* lpJniStorage = (visualizerJniStorage *)env->GetLongField(
-        thiz, fields.fidJniData);
+    VisualizerJniStorage* lpJniStorage =
+        (VisualizerJniStorage *)env->GetLongField(thiz, fields.fidJniData);
+
+    // reset the native resources in the Java object so any attempt to access
+    // them after a call to release fails.
+    env->SetLongField(thiz, fields.fidJniData, 0);
+
     if (lpJniStorage) {
         ALOGV("deleting pJniStorage: %p\n", lpJniStorage);
         delete lpJniStorage;
     }
 }
 
-// ----------------------------------------------------------------------------
-static void android_media_visualizer_native_release(JNIEnv *env,  jobject thiz) {
-
-    // do everything a call to finalize would
-    android_media_visualizer_native_finalize(env, thiz);
-    // + reset the native resources in the Java object so any attempt to access
-    // them after a call to release fails.
-    env->SetLongField(thiz, fields.fidNativeVisualizer, 0);
-    env->SetLongField(thiz, fields.fidJniData, 0);
+static void android_media_visualizer_native_finalize(JNIEnv *env,  jobject thiz) {
+    ALOGV("android_media_visualizer_native_finalize jobject: %p\n", thiz);
+    android_media_visualizer_native_release(env, thiz);
 }
 
+// ----------------------------------------------------------------------------
 static jint
 android_media_visualizer_native_setEnabled(JNIEnv *env, jobject thiz, jboolean enabled)
 {
-    Visualizer* lpVisualizer = getVisualizer(env, thiz);
-    if (lpVisualizer == NULL) {
+    sp<Visualizer> lpVisualizer = getVisualizer(env, thiz);
+    if (lpVisualizer == 0) {
         return VISUALIZER_ERROR_NO_INIT;
     }
 
     jint retVal = translateError(lpVisualizer->setEnabled(enabled));
 
     if (!enabled) {
-        visualizerJniStorage* lpJniStorage = (visualizerJniStorage *)env->GetLongField(
+        VisualizerJniStorage* lpJniStorage = (VisualizerJniStorage *)env->GetLongField(
             thiz, fields.fidJniData);
 
         if (NULL != lpJniStorage)
@@ -474,8 +484,8 @@
 static jboolean
 android_media_visualizer_native_getEnabled(JNIEnv *env, jobject thiz)
 {
-    Visualizer* lpVisualizer = getVisualizer(env, thiz);
-    if (lpVisualizer == NULL) {
+    sp<Visualizer> lpVisualizer = getVisualizer(env, thiz);
+    if (lpVisualizer == 0) {
         return JNI_FALSE;
     }
 
@@ -507,8 +517,8 @@
 static jint
 android_media_visualizer_native_setCaptureSize(JNIEnv *env, jobject thiz, jint size)
 {
-    Visualizer* lpVisualizer = getVisualizer(env, thiz);
-    if (lpVisualizer == NULL) {
+    sp<Visualizer> lpVisualizer = getVisualizer(env, thiz);
+    if (lpVisualizer == 0) {
         return VISUALIZER_ERROR_NO_INIT;
     }
 
@@ -518,8 +528,8 @@
 static jint
 android_media_visualizer_native_getCaptureSize(JNIEnv *env, jobject thiz)
 {
-    Visualizer* lpVisualizer = getVisualizer(env, thiz);
-    if (lpVisualizer == NULL) {
+    sp<Visualizer> lpVisualizer = getVisualizer(env, thiz);
+    if (lpVisualizer == 0) {
         return -1;
     }
     return (jint) lpVisualizer->getCaptureSize();
@@ -528,8 +538,8 @@
 static jint
 android_media_visualizer_native_setScalingMode(JNIEnv *env, jobject thiz, jint mode)
 {
-    Visualizer* lpVisualizer = getVisualizer(env, thiz);
-    if (lpVisualizer == NULL) {
+    sp<Visualizer> lpVisualizer = getVisualizer(env, thiz);
+    if (lpVisualizer == 0) {
         return VISUALIZER_ERROR_NO_INIT;
     }
 
@@ -539,8 +549,8 @@
 static jint
 android_media_visualizer_native_getScalingMode(JNIEnv *env, jobject thiz)
 {
-    Visualizer* lpVisualizer = getVisualizer(env, thiz);
-    if (lpVisualizer == NULL) {
+    sp<Visualizer> lpVisualizer = getVisualizer(env, thiz);
+    if (lpVisualizer == 0) {
         return -1;
     }
     return (jint)lpVisualizer->getScalingMode();
@@ -549,8 +559,8 @@
 static jint
 android_media_visualizer_native_setMeasurementMode(JNIEnv *env, jobject thiz, jint mode)
 {
-    Visualizer* lpVisualizer = getVisualizer(env, thiz);
-    if (lpVisualizer == NULL) {
+    sp<Visualizer> lpVisualizer = getVisualizer(env, thiz);
+    if (lpVisualizer == 0) {
         return VISUALIZER_ERROR_NO_INIT;
     }
     return translateError(lpVisualizer->setMeasurementMode(mode));
@@ -559,8 +569,8 @@
 static jint
 android_media_visualizer_native_getMeasurementMode(JNIEnv *env, jobject thiz)
 {
-    Visualizer* lpVisualizer = getVisualizer(env, thiz);
-    if (lpVisualizer == NULL) {
+    sp<Visualizer> lpVisualizer = getVisualizer(env, thiz);
+    if (lpVisualizer == 0) {
         return MEASUREMENT_MODE_NONE;
     }
     return lpVisualizer->getMeasurementMode();
@@ -569,8 +579,8 @@
 static jint
 android_media_visualizer_native_getSamplingRate(JNIEnv *env, jobject thiz)
 {
-    Visualizer* lpVisualizer = getVisualizer(env, thiz);
-    if (lpVisualizer == NULL) {
+    sp<Visualizer> lpVisualizer = getVisualizer(env, thiz);
+    if (lpVisualizer == 0) {
         return -1;
     }
     return (jint) lpVisualizer->getSamplingRate();
@@ -579,8 +589,8 @@
 static jint
 android_media_visualizer_native_getWaveForm(JNIEnv *env, jobject thiz, jbyteArray jWaveform)
 {
-    Visualizer* lpVisualizer = getVisualizer(env, thiz);
-    if (lpVisualizer == NULL) {
+    sp<Visualizer> lpVisualizer = getVisualizer(env, thiz);
+    if (lpVisualizer == 0) {
         return VISUALIZER_ERROR_NO_INIT;
     }
 
@@ -597,8 +607,8 @@
 static jint
 android_media_visualizer_native_getFft(JNIEnv *env, jobject thiz, jbyteArray jFft)
 {
-    Visualizer* lpVisualizer = getVisualizer(env, thiz);
-    if (lpVisualizer == NULL) {
+    sp<Visualizer> lpVisualizer = getVisualizer(env, thiz);
+    if (lpVisualizer == 0) {
         return VISUALIZER_ERROR_NO_INIT;
     }
 
@@ -616,8 +626,8 @@
 static jint
 android_media_visualizer_native_getPeakRms(JNIEnv *env, jobject thiz, jobject jPeakRmsObj)
 {
-    Visualizer* lpVisualizer = getVisualizer(env, thiz);
-    if (lpVisualizer == NULL) {
+    sp<Visualizer> lpVisualizer = getVisualizer(env, thiz);
+    if (lpVisualizer == 0) {
         return VISUALIZER_ERROR_NO_INIT;
     }
     int32_t measurements[2];
@@ -635,11 +645,11 @@
 static jint
 android_media_setPeriodicCapture(JNIEnv *env, jobject thiz, jint rate, jboolean jWaveform, jboolean jFft)
 {
-    Visualizer* lpVisualizer = getVisualizer(env, thiz);
-    if (lpVisualizer == NULL) {
+    sp<Visualizer> lpVisualizer = getVisualizer(env, thiz);
+    if (lpVisualizer == 0) {
         return VISUALIZER_ERROR_NO_INIT;
     }
-    visualizerJniStorage* lpJniStorage = (visualizerJniStorage *)env->GetLongField(thiz,
+    VisualizerJniStorage* lpJniStorage = (VisualizerJniStorage *)env->GetLongField(thiz,
             fields.fidJniData);
     if (lpJniStorage == NULL) {
         return VISUALIZER_ERROR_NO_INIT;
diff --git a/opengl/java/android/opengl/GLSurfaceView.java b/opengl/java/android/opengl/GLSurfaceView.java
index 3c76115..359a7a9 100644
--- a/opengl/java/android/opengl/GLSurfaceView.java
+++ b/opengl/java/android/opengl/GLSurfaceView.java
@@ -32,6 +32,7 @@
 import android.content.Context;
 import android.content.pm.ConfigurationInfo;
 import android.os.SystemProperties;
+import android.os.Trace;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.SurfaceHolder;
@@ -1497,7 +1498,12 @@
                         }
                         GLSurfaceView view = mGLSurfaceViewWeakRef.get();
                         if (view != null) {
-                            view.mRenderer.onSurfaceCreated(gl, mEglHelper.mEglConfig);
+                            try {
+                                Trace.traceBegin(Trace.TRACE_TAG_VIEW, "onSurfaceCreated");
+                                view.mRenderer.onSurfaceCreated(gl, mEglHelper.mEglConfig);
+                            } finally {
+                                Trace.traceEnd(Trace.TRACE_TAG_VIEW);
+                            }
                         }
                         createEglContext = false;
                     }
@@ -1508,7 +1514,12 @@
                         }
                         GLSurfaceView view = mGLSurfaceViewWeakRef.get();
                         if (view != null) {
-                            view.mRenderer.onSurfaceChanged(gl, w, h);
+                            try {
+                                Trace.traceBegin(Trace.TRACE_TAG_VIEW, "onSurfaceChanged");
+                                view.mRenderer.onSurfaceChanged(gl, w, h);
+                            } finally {
+                                Trace.traceEnd(Trace.TRACE_TAG_VIEW);
+                            }
                         }
                         sizeChanged = false;
                     }
@@ -1519,7 +1530,12 @@
                     {
                         GLSurfaceView view = mGLSurfaceViewWeakRef.get();
                         if (view != null) {
-                            view.mRenderer.onDrawFrame(gl);
+                            try {
+                                Trace.traceBegin(Trace.TRACE_TAG_VIEW, "onDrawFrame");
+                                view.mRenderer.onDrawFrame(gl);
+                            } finally {
+                                Trace.traceEnd(Trace.TRACE_TAG_VIEW);
+                            }
                         }
                     }
                     int swapError = mEglHelper.swap();
diff --git a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
index d876264..ddbcd78 100644
--- a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
+++ b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
@@ -336,12 +336,24 @@
 
         @Override
         public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
-            Log.w(TAG, "SSL error; displaying SSL warning.");
+            Log.w(TAG, "SSL error (error: " + error.getPrimaryError() + " host: " +
+                    // Only show host to avoid leaking private info.
+                    Uri.parse(error.getUrl()).getHost() + " certificate: " +
+                    error.getCertificate() + "); displaying SSL warning.");
             final String html = String.format(SSL_ERROR_HTML, getString(R.string.ssl_error_warning),
                     getString(R.string.ssl_error_example), mBrowserBailOutToken,
                     getString(R.string.ssl_error_continue));
             view.loadDataWithBaseURL(INTERNAL_ASSETS, html, "text/HTML", "UTF-8", null);
         }
+
+        @Override
+        public boolean shouldOverrideUrlLoading (WebView view, String url) {
+            if (url.startsWith("tel:")) {
+                startActivity(new Intent(Intent.ACTION_DIAL, Uri.parse(url)));
+                return true;
+            }
+            return false;
+        }
     }
 
     private class MyWebChromeClient extends WebChromeClient {
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_breadcrumb_arrow_am_alpha.png b/packages/DocumentsUI/res/drawable-hdpi/ic_breadcrumb_arrow_am_alpha.png
index 95b2b6d..67f890c 100644
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_breadcrumb_arrow_am_alpha.png
+++ b/packages/DocumentsUI/res/drawable-hdpi/ic_breadcrumb_arrow_am_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_cab_accept_alpha.png b/packages/DocumentsUI/res/drawable-hdpi/ic_cab_accept_alpha.png
deleted file mode 100644
index ddf9a80..0000000
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_cab_accept_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_cab_cancel_alpha.png b/packages/DocumentsUI/res/drawable-hdpi/ic_cab_cancel_alpha.png
index 7168197..1a9cd75 100644
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_cab_cancel_alpha.png
+++ b/packages/DocumentsUI/res/drawable-hdpi/ic_cab_cancel_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_cab_select_item_alpha.png b/packages/DocumentsUI/res/drawable-hdpi/ic_cab_select_item_alpha.png
deleted file mode 100644
index 1c6ec6a..0000000
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_cab_select_item_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_dialog_alert_alpha.png b/packages/DocumentsUI/res/drawable-hdpi/ic_dialog_alert_alpha.png
index 11546a743..4c3d9a4 100644
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_dialog_alert_alpha.png
+++ b/packages/DocumentsUI/res/drawable-hdpi/ic_dialog_alert_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_dialog_info_alpha.png b/packages/DocumentsUI/res/drawable-hdpi/ic_dialog_info_alpha.png
index de5c113..da56077 100644
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_dialog_info_alpha.png
+++ b/packages/DocumentsUI/res/drawable-hdpi/ic_dialog_info_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_album_alpha.png b/packages/DocumentsUI/res/drawable-hdpi/ic_doc_album_alpha.png
index 769144a..2b21c12 100644
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_album_alpha.png
+++ b/packages/DocumentsUI/res/drawable-hdpi/ic_doc_album_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_apk_alpha.png b/packages/DocumentsUI/res/drawable-hdpi/ic_doc_apk_alpha.png
index 22f95b9..ed3ee45 100644
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_apk_alpha.png
+++ b/packages/DocumentsUI/res/drawable-hdpi/ic_doc_apk_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_audio_alpha.png b/packages/DocumentsUI/res/drawable-hdpi/ic_doc_audio_alpha.png
index c5152f4..1a3ebc47 100644
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_doc_audio_alpha.png
+++ b/packages/DocumentsUI/res/drawable-hdpi/ic_doc_audio_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_folder_alpha.png b/packages/DocumentsUI/res/drawable-hdpi/ic_folder_alpha.png
deleted file mode 100644
index eb71f81..0000000
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_folder_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_menu_copy_alpha.png b/packages/DocumentsUI/res/drawable-hdpi/ic_menu_copy_alpha.png
index 4997173..9a9e570 100644
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_menu_copy_alpha.png
+++ b/packages/DocumentsUI/res/drawable-hdpi/ic_menu_copy_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_menu_disconnect_am_alpha.png b/packages/DocumentsUI/res/drawable-hdpi/ic_menu_disconnect_am_alpha.png
deleted file mode 100644
index 59eef4d..0000000
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_menu_disconnect_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_menu_new_folder_am_alpha.png b/packages/DocumentsUI/res/drawable-hdpi/ic_menu_new_folder_am_alpha.png
index cd8987a..1d25a2d 100644
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_menu_new_folder_am_alpha.png
+++ b/packages/DocumentsUI/res/drawable-hdpi/ic_menu_new_folder_am_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_menu_overflow_alpha.png b/packages/DocumentsUI/res/drawable-hdpi/ic_menu_overflow_alpha.png
deleted file mode 100644
index 95716b3..0000000
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_menu_overflow_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_menu_rename_am_alpha.png b/packages/DocumentsUI/res/drawable-hdpi/ic_menu_rename_am_alpha.png
deleted file mode 100644
index 76394c4..0000000
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_menu_rename_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_menu_search_alpha.png b/packages/DocumentsUI/res/drawable-hdpi/ic_menu_search_alpha.png
index 941b93c..c593e7a 100644
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_menu_search_alpha.png
+++ b/packages/DocumentsUI/res/drawable-hdpi/ic_menu_search_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_menu_settings_alpha.png b/packages/DocumentsUI/res/drawable-hdpi/ic_menu_settings_alpha.png
deleted file mode 100644
index 4f5bcea..0000000
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_menu_settings_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_menu_sortby_am_alpha.png b/packages/DocumentsUI/res/drawable-hdpi/ic_menu_sortby_am_alpha.png
index fad19c1..5e66488 100644
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_menu_sortby_am_alpha.png
+++ b/packages/DocumentsUI/res/drawable-hdpi/ic_menu_sortby_am_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_menu_undo_am_alpha.png b/packages/DocumentsUI/res/drawable-hdpi/ic_menu_undo_am_alpha.png
deleted file mode 100644
index df94033..0000000
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_menu_undo_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_menu_view_grid_alpha.png b/packages/DocumentsUI/res/drawable-hdpi/ic_menu_view_grid_alpha.png
index 87154ad..7e15a8c 100644
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_menu_view_grid_alpha.png
+++ b/packages/DocumentsUI/res/drawable-hdpi/ic_menu_view_grid_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_menu_view_list_alpha.png b/packages/DocumentsUI/res/drawable-hdpi/ic_menu_view_list_alpha.png
index 9b40dae..c15537a 100644
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_menu_view_list_alpha.png
+++ b/packages/DocumentsUI/res/drawable-hdpi/ic_menu_view_list_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_open_am_alpha.png b/packages/DocumentsUI/res/drawable-hdpi/ic_open_am_alpha.png
deleted file mode 100644
index 6b0d909..0000000
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_open_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_popout_am_alpha.png b/packages/DocumentsUI/res/drawable-hdpi/ic_popout_am_alpha.png
deleted file mode 100644
index e1c39f8..0000000
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_popout_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_root_download_alpha.png b/packages/DocumentsUI/res/drawable-hdpi/ic_root_download_alpha.png
index 092556a..d9aacea 100644
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_root_download_alpha.png
+++ b/packages/DocumentsUI/res/drawable-hdpi/ic_root_download_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_root_folder_am_alpha.png b/packages/DocumentsUI/res/drawable-hdpi/ic_root_folder_am_alpha.png
deleted file mode 100644
index 223e619..0000000
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_root_folder_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_root_recent_alpha.png b/packages/DocumentsUI/res/drawable-hdpi/ic_root_recent_alpha.png
index 37fbdf7..9e003f0 100644
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_root_recent_alpha.png
+++ b/packages/DocumentsUI/res/drawable-hdpi/ic_root_recent_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_root_sdcard_alpha.png b/packages/DocumentsUI/res/drawable-hdpi/ic_root_sdcard_alpha.png
index 0929f93..65e42aa 100644
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_root_sdcard_alpha.png
+++ b/packages/DocumentsUI/res/drawable-hdpi/ic_root_sdcard_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_root_usb_light_alpha.png b/packages/DocumentsUI/res/drawable-hdpi/ic_root_usb_light_alpha.png
deleted file mode 100644
index 738ef84..0000000
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_root_usb_light_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_breadcrumb_arrow_am_alpha.png b/packages/DocumentsUI/res/drawable-mdpi/ic_breadcrumb_arrow_am_alpha.png
index 897f7a9..9a048f1 100644
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_breadcrumb_arrow_am_alpha.png
+++ b/packages/DocumentsUI/res/drawable-mdpi/ic_breadcrumb_arrow_am_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_cab_accept_alpha.png b/packages/DocumentsUI/res/drawable-mdpi/ic_cab_accept_alpha.png
deleted file mode 100644
index c9224a6..0000000
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_cab_accept_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_cab_cancel_alpha.png b/packages/DocumentsUI/res/drawable-mdpi/ic_cab_cancel_alpha.png
index 3bb74f9..40a1a84 100644
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_cab_cancel_alpha.png
+++ b/packages/DocumentsUI/res/drawable-mdpi/ic_cab_cancel_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_cab_select_item_alpha.png b/packages/DocumentsUI/res/drawable-mdpi/ic_cab_select_item_alpha.png
deleted file mode 100644
index 2d47376..0000000
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_cab_select_item_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_dialog_alert_alpha.png b/packages/DocumentsUI/res/drawable-mdpi/ic_dialog_alert_alpha.png
index 3868210..e768d11 100644
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_dialog_alert_alpha.png
+++ b/packages/DocumentsUI/res/drawable-mdpi/ic_dialog_alert_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_dialog_info_alpha.png b/packages/DocumentsUI/res/drawable-mdpi/ic_dialog_info_alpha.png
index a74854d..5ef3dc0 100644
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_dialog_info_alpha.png
+++ b/packages/DocumentsUI/res/drawable-mdpi/ic_dialog_info_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_album_alpha.png b/packages/DocumentsUI/res/drawable-mdpi/ic_doc_album_alpha.png
index f5e45a7..ac27eea 100644
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_album_alpha.png
+++ b/packages/DocumentsUI/res/drawable-mdpi/ic_doc_album_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_apk_alpha.png b/packages/DocumentsUI/res/drawable-mdpi/ic_doc_apk_alpha.png
index 67e33ce..a4add51 100644
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_apk_alpha.png
+++ b/packages/DocumentsUI/res/drawable-mdpi/ic_doc_apk_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_audio_alpha.png b/packages/DocumentsUI/res/drawable-mdpi/ic_doc_audio_alpha.png
index 34093d0..a9a7f20 100644
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_doc_audio_alpha.png
+++ b/packages/DocumentsUI/res/drawable-mdpi/ic_doc_audio_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_folder_alpha.png b/packages/DocumentsUI/res/drawable-mdpi/ic_folder_alpha.png
deleted file mode 100644
index 5ecddc5..0000000
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_folder_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_menu_copy_alpha.png b/packages/DocumentsUI/res/drawable-mdpi/ic_menu_copy_alpha.png
index f8a5262..c94cc28 100644
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_menu_copy_alpha.png
+++ b/packages/DocumentsUI/res/drawable-mdpi/ic_menu_copy_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_menu_disconnect_am_alpha.png b/packages/DocumentsUI/res/drawable-mdpi/ic_menu_disconnect_am_alpha.png
deleted file mode 100644
index d8ebf49..0000000
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_menu_disconnect_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_menu_new_folder_am_alpha.png b/packages/DocumentsUI/res/drawable-mdpi/ic_menu_new_folder_am_alpha.png
index 62c47d5..6e6b870 100644
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_menu_new_folder_am_alpha.png
+++ b/packages/DocumentsUI/res/drawable-mdpi/ic_menu_new_folder_am_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_menu_overflow_alpha.png b/packages/DocumentsUI/res/drawable-mdpi/ic_menu_overflow_alpha.png
deleted file mode 100644
index 0825647..0000000
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_menu_overflow_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_menu_rename_am_alpha.png b/packages/DocumentsUI/res/drawable-mdpi/ic_menu_rename_am_alpha.png
deleted file mode 100644
index 9d9e3d7..0000000
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_menu_rename_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_menu_search_alpha.png b/packages/DocumentsUI/res/drawable-mdpi/ic_menu_search_alpha.png
index 9fc1df6..6b16343 100644
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_menu_search_alpha.png
+++ b/packages/DocumentsUI/res/drawable-mdpi/ic_menu_search_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_menu_settings_alpha.png b/packages/DocumentsUI/res/drawable-mdpi/ic_menu_settings_alpha.png
deleted file mode 100644
index c95381b..0000000
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_menu_settings_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_menu_sortby_am_alpha.png b/packages/DocumentsUI/res/drawable-mdpi/ic_menu_sortby_am_alpha.png
index f95d30d..04a12a4 100644
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_menu_sortby_am_alpha.png
+++ b/packages/DocumentsUI/res/drawable-mdpi/ic_menu_sortby_am_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_menu_undo_am_alpha.png b/packages/DocumentsUI/res/drawable-mdpi/ic_menu_undo_am_alpha.png
deleted file mode 100644
index 47c00dd..0000000
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_menu_undo_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_menu_view_grid_alpha.png b/packages/DocumentsUI/res/drawable-mdpi/ic_menu_view_grid_alpha.png
index fe2c78c..5f968d5 100644
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_menu_view_grid_alpha.png
+++ b/packages/DocumentsUI/res/drawable-mdpi/ic_menu_view_grid_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_menu_view_list_alpha.png b/packages/DocumentsUI/res/drawable-mdpi/ic_menu_view_list_alpha.png
index 8b3a303..7a8eae9 100644
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_menu_view_list_alpha.png
+++ b/packages/DocumentsUI/res/drawable-mdpi/ic_menu_view_list_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_open_am_alpha.png b/packages/DocumentsUI/res/drawable-mdpi/ic_open_am_alpha.png
deleted file mode 100644
index 5923c08..0000000
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_open_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_popout_am_alpha.png b/packages/DocumentsUI/res/drawable-mdpi/ic_popout_am_alpha.png
deleted file mode 100644
index 0e45037..0000000
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_popout_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_root_download_alpha.png b/packages/DocumentsUI/res/drawable-mdpi/ic_root_download_alpha.png
index a55f4ca..c2c845e 100644
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_root_download_alpha.png
+++ b/packages/DocumentsUI/res/drawable-mdpi/ic_root_download_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_root_folder_am_alpha.png b/packages/DocumentsUI/res/drawable-mdpi/ic_root_folder_am_alpha.png
deleted file mode 100644
index 993e060..0000000
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_root_folder_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_root_recent_alpha.png b/packages/DocumentsUI/res/drawable-mdpi/ic_root_recent_alpha.png
index f359c4be..f500d58 100644
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_root_recent_alpha.png
+++ b/packages/DocumentsUI/res/drawable-mdpi/ic_root_recent_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_root_sdcard_alpha.png b/packages/DocumentsUI/res/drawable-mdpi/ic_root_sdcard_alpha.png
index dd76d07..1de8292 100644
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_root_sdcard_alpha.png
+++ b/packages/DocumentsUI/res/drawable-mdpi/ic_root_sdcard_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_root_usb_light_alpha.png b/packages/DocumentsUI/res/drawable-mdpi/ic_root_usb_light_alpha.png
deleted file mode 100644
index fccd45e..0000000
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_root_usb_light_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_breadcrumb_arrow_am_alpha.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_breadcrumb_arrow_am_alpha.png
index 5d0805e..073583e 100644
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_breadcrumb_arrow_am_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xhdpi/ic_breadcrumb_arrow_am_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_cab_accept_alpha.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_cab_accept_alpha.png
deleted file mode 100644
index 1a3d7c4..0000000
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_cab_accept_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_cab_cancel_alpha.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_cab_cancel_alpha.png
index 14b9aef..6bc4372 100644
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_cab_cancel_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xhdpi/ic_cab_cancel_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_cab_select_item_alpha.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_cab_select_item_alpha.png
deleted file mode 100644
index 953ab62..0000000
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_cab_select_item_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_dialog_alert_alpha.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_dialog_alert_alpha.png
index 5198b84..2ea6164 100644
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_dialog_alert_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xhdpi/ic_dialog_alert_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_dialog_info_alpha.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_dialog_info_alpha.png
index cdf8c95..46ed12a 100644
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_dialog_info_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xhdpi/ic_dialog_info_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_album_alpha.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_album_alpha.png
index aecd4cf..4203d35 100644
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_album_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_album_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_apk_alpha.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_apk_alpha.png
index 24ad2dd..41558f2 100644
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_apk_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_apk_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_audio_alpha.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_audio_alpha.png
index bf9dbde..e190c4d 100644
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_audio_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xhdpi/ic_doc_audio_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_folder_alpha.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_folder_alpha.png
deleted file mode 100644
index cd24dfe..0000000
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_folder_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_copy_alpha.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_copy_alpha.png
index 9afd1a1..1cf76a9 100644
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_copy_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_copy_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_disconnect_am_alpha.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_disconnect_am_alpha.png
deleted file mode 100644
index 95b0bb8..0000000
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_disconnect_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_new_folder_am_alpha.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_new_folder_am_alpha.png
index 14e96e2..49272b0 100644
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_new_folder_am_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_new_folder_am_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_overflow_alpha.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_overflow_alpha.png
deleted file mode 100644
index 311e4dc..0000000
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_overflow_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_rename_am_alpha.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_rename_am_alpha.png
deleted file mode 100644
index e2e3925..0000000
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_rename_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_search_alpha.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_search_alpha.png
index 6b2ddad..6381902 100644
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_search_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_search_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_settings_alpha.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_settings_alpha.png
deleted file mode 100644
index 304435a..0000000
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_settings_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_sortby_am_alpha.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_sortby_am_alpha.png
index 27e6acf..9e4fd61 100644
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_sortby_am_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_sortby_am_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_undo_am_alpha.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_undo_am_alpha.png
deleted file mode 100644
index 146b196..0000000
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_undo_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_view_grid_alpha.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_view_grid_alpha.png
index cc4d40c..630188c 100644
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_view_grid_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_view_grid_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_view_list_alpha.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_view_list_alpha.png
index 6c897c4..73372f4 100644
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_view_list_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xhdpi/ic_menu_view_list_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_open_am_alpha.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_open_am_alpha.png
deleted file mode 100644
index be6fb5d..0000000
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_open_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_popout_am_alpha.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_popout_am_alpha.png
deleted file mode 100644
index 7ba284f..0000000
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_popout_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_root_download_alpha.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_root_download_alpha.png
index 9bda62c..f5afb24 100644
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_root_download_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xhdpi/ic_root_download_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_root_folder_am_alpha.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_root_folder_am_alpha.png
deleted file mode 100644
index 24dce60..0000000
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_root_folder_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_root_recent_alpha.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_root_recent_alpha.png
index 5ef3288..fe71c25 100644
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_root_recent_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xhdpi/ic_root_recent_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_root_sdcard_alpha.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_root_sdcard_alpha.png
index 99b8054..00b8a8b7 100644
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_root_sdcard_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xhdpi/ic_root_sdcard_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_root_usb_light_alpha.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_root_usb_light_alpha.png
deleted file mode 100644
index 80717e4..0000000
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_root_usb_light_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_breadcrumb_arrow_am_alpha.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_breadcrumb_arrow_am_alpha.png
index 02a988b..b96cfd1 100644
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_breadcrumb_arrow_am_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xxhdpi/ic_breadcrumb_arrow_am_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_cab_accept_alpha.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_cab_accept_alpha.png
deleted file mode 100644
index a23a9ae..0000000
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_cab_accept_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_cab_cancel_alpha.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_cab_cancel_alpha.png
index b10db3e..51b4401 100644
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_cab_cancel_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xxhdpi/ic_cab_cancel_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_cab_select_item_alpha.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_cab_select_item_alpha.png
deleted file mode 100644
index 3606fb3..0000000
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_cab_select_item_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_dialog_alert_alpha.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_dialog_alert_alpha.png
index 0a54598..ed36f70 100644
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_dialog_alert_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xxhdpi/ic_dialog_alert_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_dialog_info_alpha.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_dialog_info_alpha.png
index 6d3260c..a81eeb9 100644
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_dialog_info_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xxhdpi/ic_dialog_info_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_album_alpha.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_album_alpha.png
index 31dc7a4..60f59f5 100644
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_album_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_album_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_apk_alpha.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_apk_alpha.png
index 5d0f0db..6006b12 100644
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_apk_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_apk_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_audio_alpha.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_audio_alpha.png
index 8116b62..7926188 100644
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_audio_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xxhdpi/ic_doc_audio_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_folder_alpha.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_folder_alpha.png
deleted file mode 100644
index 02249d2..0000000
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_folder_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_copy_alpha.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_copy_alpha.png
index 90d1a09..074ea88 100644
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_copy_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_copy_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_disconnect_am_alpha.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_disconnect_am_alpha.png
deleted file mode 100644
index 7a993b9..0000000
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_disconnect_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_new_folder_am_alpha.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_new_folder_am_alpha.png
index 12d279b..5c4360a 100644
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_new_folder_am_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_new_folder_am_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_overflow_alpha.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_overflow_alpha.png
deleted file mode 100644
index 325c48a..0000000
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_overflow_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_rename_am_alpha.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_rename_am_alpha.png
deleted file mode 100644
index ee2465d..0000000
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_rename_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_search_alpha.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_search_alpha.png
index 538fbe5..3ae490e 100644
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_search_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_search_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_settings_alpha.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_settings_alpha.png
deleted file mode 100644
index d99206c..0000000
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_settings_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_sortby_am_alpha.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_sortby_am_alpha.png
index 4f1355c..cb9d196 100644
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_sortby_am_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_sortby_am_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_undo_am_alpha.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_undo_am_alpha.png
deleted file mode 100644
index d5cc56f..0000000
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_undo_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_view_grid_alpha.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_view_grid_alpha.png
index de96c3f..7560f62 100644
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_view_grid_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_view_grid_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_view_list_alpha.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_view_list_alpha.png
index 7805834..b9483c3 100644
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_view_list_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xxhdpi/ic_menu_view_list_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_open_am_alpha.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_open_am_alpha.png
deleted file mode 100644
index 7ed919c..0000000
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_open_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_popout_am_alpha.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_popout_am_alpha.png
deleted file mode 100644
index 756b684..0000000
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_popout_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_root_download_alpha.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_root_download_alpha.png
index ffb9841..ce97c85 100644
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_root_download_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xxhdpi/ic_root_download_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_root_folder_am_alpha.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_root_folder_am_alpha.png
deleted file mode 100644
index 305a6b8..0000000
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_root_folder_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_root_recent_alpha.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_root_recent_alpha.png
index dc1425d..1dad2a8 100644
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_root_recent_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xxhdpi/ic_root_recent_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_root_sdcard_alpha.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_root_sdcard_alpha.png
index cea8ac0..5236774 100644
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_root_sdcard_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xxhdpi/ic_root_sdcard_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_root_usb_light_alpha.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_root_usb_light_alpha.png
deleted file mode 100644
index e0a1d23a..0000000
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_root_usb_light_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_breadcrumb_arrow_am_alpha.png b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_breadcrumb_arrow_am_alpha.png
index 84573f6..6f2dc5b 100644
--- a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_breadcrumb_arrow_am_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_breadcrumb_arrow_am_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_cab_accept_alpha.png b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_cab_accept_alpha.png
deleted file mode 100644
index 986d3fb..0000000
--- a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_cab_accept_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_cab_cancel_alpha.png b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_cab_cancel_alpha.png
index ce443f9..df42fee 100644
--- a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_cab_cancel_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_cab_cancel_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_cab_select_item_alpha.png b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_cab_select_item_alpha.png
deleted file mode 100644
index a54e0ea..0000000
--- a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_cab_select_item_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_dialog_alert_alpha.png b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_dialog_alert_alpha.png
index 6e65716..3f4d539 100644
--- a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_dialog_alert_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_dialog_alert_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_dialog_info_alpha.png b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_dialog_info_alpha.png
index 35bd56e..c8f86b9 100644
--- a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_dialog_info_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_dialog_info_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_doc_album_alpha.png b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_doc_album_alpha.png
index 64aa50b..d2dd494 100644
--- a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_doc_album_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_doc_album_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_doc_apk_alpha.png b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_doc_apk_alpha.png
index 7cbcb8b..4f935bf 100644
--- a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_doc_apk_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_doc_apk_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_doc_audio_alpha.png b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_doc_audio_alpha.png
index d6d79ec..7499cbc 100644
--- a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_doc_audio_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_doc_audio_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_folder_alpha.png b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_folder_alpha.png
deleted file mode 100644
index c0c27a2..0000000
--- a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_folder_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_menu_copy_alpha.png b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_menu_copy_alpha.png
index 2dcf03e..1f6af72 100644
--- a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_menu_copy_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_menu_copy_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_menu_disconnect_am_alpha.png b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_menu_disconnect_am_alpha.png
deleted file mode 100644
index d7eb04f..0000000
--- a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_menu_disconnect_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_menu_new_folder_am_alpha.png b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_menu_new_folder_am_alpha.png
index 879bf45..073d8533 100644
--- a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_menu_new_folder_am_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_menu_new_folder_am_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_menu_overflow_alpha.png b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_menu_overflow_alpha.png
deleted file mode 100644
index 2cd4137..0000000
--- a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_menu_overflow_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_menu_rename_am_alpha.png b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_menu_rename_am_alpha.png
deleted file mode 100644
index 268049e..0000000
--- a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_menu_rename_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_menu_search_alpha.png b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_menu_search_alpha.png
index 78fc61d..21be572 100644
--- a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_menu_search_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_menu_search_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_menu_settings_alpha.png b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_menu_settings_alpha.png
deleted file mode 100644
index 36e8e68..0000000
--- a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_menu_settings_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_menu_sortby_am_alpha.png b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_menu_sortby_am_alpha.png
index 827a92c..631663a 100644
--- a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_menu_sortby_am_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_menu_sortby_am_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_menu_undo_am_alpha.png b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_menu_undo_am_alpha.png
deleted file mode 100644
index 0f12b7d..0000000
--- a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_menu_undo_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_menu_view_grid_alpha.png b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_menu_view_grid_alpha.png
index df1bac1..5d1e8d9 100644
--- a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_menu_view_grid_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_menu_view_grid_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_menu_view_list_alpha.png b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_menu_view_list_alpha.png
index 9420374..7c1506b 100644
--- a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_menu_view_list_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_menu_view_list_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_open_am_alpha.png b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_open_am_alpha.png
deleted file mode 100644
index 0394c5c..0000000
--- a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_open_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_popout_am_alpha.png b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_popout_am_alpha.png
deleted file mode 100644
index 325d374..0000000
--- a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_popout_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_root_download_alpha.png b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_root_download_alpha.png
index 87a5210..8c83bff 100644
--- a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_root_download_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_root_download_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_root_folder_am_alpha.png b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_root_folder_am_alpha.png
deleted file mode 100644
index bb41a91..0000000
--- a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_root_folder_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_root_recent_alpha.png b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_root_recent_alpha.png
index baa723d..68df974 100644
--- a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_root_recent_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_root_recent_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_root_sdcard_alpha.png b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_root_sdcard_alpha.png
index 39c737f..c7daa2a 100644
--- a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_root_sdcard_alpha.png
+++ b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_root_sdcard_alpha.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_root_usb_light_alpha.png b/packages/DocumentsUI/res/drawable-xxxhdpi/ic_root_usb_light_alpha.png
deleted file mode 100644
index 88973e0..0000000
--- a/packages/DocumentsUI/res/drawable-xxxhdpi/ic_root_usb_light_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable/ic_cab_accept.xml b/packages/DocumentsUI/res/drawable/ic_cab_accept.xml
deleted file mode 100644
index cda28453..0000000
--- a/packages/DocumentsUI/res/drawable/ic_cab_accept.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
-    android:src="@drawable/ic_cab_accept_alpha"
-    android:tint="?android:attr/colorControlNormal" />
diff --git a/packages/DocumentsUI/res/drawable/ic_cab_select_item.xml b/packages/DocumentsUI/res/drawable/ic_cab_select_item.xml
deleted file mode 100644
index e440ee9..0000000
--- a/packages/DocumentsUI/res/drawable/ic_cab_select_item.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
-    android:src="@drawable/ic_cab_select_item_alpha"
-    android:tint="?android:attr/colorControlNormal" />
diff --git a/packages/DocumentsUI/res/drawable/ic_folder.xml b/packages/DocumentsUI/res/drawable/ic_folder.xml
deleted file mode 100644
index 8adaf3b..0000000
--- a/packages/DocumentsUI/res/drawable/ic_folder.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
-    android:src="@drawable/ic_folder_alpha"
-    android:tint="?android:attr/colorControlNormal" />
diff --git a/packages/DocumentsUI/res/drawable/ic_menu_disconnect.xml b/packages/DocumentsUI/res/drawable/ic_menu_disconnect.xml
deleted file mode 100644
index 15c5c39..0000000
--- a/packages/DocumentsUI/res/drawable/ic_menu_disconnect.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
-    android:src="@drawable/ic_menu_disconnect_am_alpha"
-    android:tint="?android:attr/colorControlNormal"
-    android:autoMirrored="true" />
diff --git a/packages/DocumentsUI/res/drawable/ic_menu_overflow.xml b/packages/DocumentsUI/res/drawable/ic_menu_overflow.xml
deleted file mode 100644
index a579b60..0000000
--- a/packages/DocumentsUI/res/drawable/ic_menu_overflow.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
-    android:src="@drawable/ic_menu_overflow_alpha"
-    android:tint="?android:attr/colorControlNormal" />
diff --git a/packages/DocumentsUI/res/drawable/ic_menu_rename.xml b/packages/DocumentsUI/res/drawable/ic_menu_rename.xml
deleted file mode 100644
index b5ba30c..0000000
--- a/packages/DocumentsUI/res/drawable/ic_menu_rename.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
-    android:src="@drawable/ic_menu_rename_am_alpha"
-    android:tint="?android:attr/colorControlNormal"
-    android:autoMirrored="true" />
diff --git a/packages/DocumentsUI/res/drawable/ic_menu_settings.xml b/packages/DocumentsUI/res/drawable/ic_menu_settings.xml
deleted file mode 100644
index 7606e75..0000000
--- a/packages/DocumentsUI/res/drawable/ic_menu_settings.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
-    android:src="@drawable/ic_menu_settings_alpha"
-    android:tint="?android:attr/colorControlNormal" />
diff --git a/packages/DocumentsUI/res/drawable/ic_menu_undo.xml b/packages/DocumentsUI/res/drawable/ic_menu_undo.xml
deleted file mode 100644
index d66ba6d..0000000
--- a/packages/DocumentsUI/res/drawable/ic_menu_undo.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
-    android:src="@drawable/ic_menu_undo_am_alpha"
-    android:tint="?android:attr/colorControlNormal"
-    android:autoMirrored="true" />
diff --git a/packages/DocumentsUI/res/drawable/ic_open.xml b/packages/DocumentsUI/res/drawable/ic_open.xml
deleted file mode 100644
index e3f1faf..0000000
--- a/packages/DocumentsUI/res/drawable/ic_open.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
-    android:src="@drawable/ic_open_am_alpha"
-    android:tint="?android:attr/colorControlNormal"
-    android:autoMirrored="true" />
diff --git a/packages/DocumentsUI/res/drawable/ic_popout.xml b/packages/DocumentsUI/res/drawable/ic_popout.xml
deleted file mode 100644
index e7d0f75..0000000
--- a/packages/DocumentsUI/res/drawable/ic_popout.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
-    android:src="@drawable/ic_popout_am_alpha"
-    android:tint="?android:attr/colorControlNormal"
-    android:autoMirrored="true" />
diff --git a/packages/DocumentsUI/res/drawable/ic_root_folder.xml b/packages/DocumentsUI/res/drawable/ic_root_folder.xml
deleted file mode 100644
index 01df07b..0000000
--- a/packages/DocumentsUI/res/drawable/ic_root_folder.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
-    android:src="@drawable/ic_root_folder_am_alpha"
-    android:tint="?android:attr/colorControlNormal"
-    android:autoMirrored="true" />
diff --git a/packages/DocumentsUI/res/drawable/ic_root_usb_light.xml b/packages/DocumentsUI/res/drawable/ic_root_usb_light.xml
deleted file mode 100644
index 5f80dd9..0000000
--- a/packages/DocumentsUI/res/drawable/ic_root_usb_light.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
-    android:src="@drawable/ic_root_usb_light_alpha"
-    android:tint="?android:attr/colorControlNormal" />
diff --git a/packages/DocumentsUI/res/layout-sw720dp/activity.xml b/packages/DocumentsUI/res/layout-sw720dp/activity.xml
index 7b40a0f..a9f1b3c 100644
--- a/packages/DocumentsUI/res/layout-sw720dp/activity.xml
+++ b/packages/DocumentsUI/res/layout-sw720dp/activity.xml
@@ -41,7 +41,9 @@
         android:layout_height="0dp"
         android:layout_weight="1"
         android:orientation="horizontal"
-        android:baselineAligned="false">
+        android:baselineAligned="false"
+        android:divider="?android:attr/dividerVertical"
+        android:showDividers="middle">
 
         <FrameLayout
             android:id="@+id/container_roots"
diff --git a/packages/SettingsLib/AndroidManifest.xml b/packages/SettingsLib/AndroidManifest.xml
index eacafd5..3873593 100644
--- a/packages/SettingsLib/AndroidManifest.xml
+++ b/packages/SettingsLib/AndroidManifest.xml
@@ -16,5 +16,9 @@
 -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-        package="com.android.settingslib">
+    package="com.android.settingslib">
+
+    <uses-sdk
+        android:minSdkVersion="21" />
+
 </manifest>
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
new file mode 100644
index 0000000..b5e53c0
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
@@ -0,0 +1,1347 @@
+/*
+ * 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.settingslib.applications;
+
+import android.app.ActivityManager;
+import android.app.AppGlobals;
+import android.app.Application;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
+import android.content.pm.IPackageStatsObserver;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageStats;
+import android.content.pm.ParceledListSlice;
+import android.content.pm.ResolveInfo;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.text.format.Formatter;
+import android.util.Log;
+import android.util.SparseArray;
+
+import java.io.File;
+import java.text.Collator;
+import java.text.Normalizer;
+import java.text.Normalizer.Form;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Objects;
+import java.util.regex.Pattern;
+
+/**
+ * Keeps track of information about all installed applications, lazy-loading
+ * as needed.
+ */
+public class ApplicationsState {
+    static final String TAG = "ApplicationsState";
+    static final boolean DEBUG = false;
+    static final boolean DEBUG_LOCKING = false;
+
+    public static final int SIZE_UNKNOWN = -1;
+    public static final int SIZE_INVALID = -2;
+
+    static final Pattern REMOVE_DIACRITICALS_PATTERN
+            = Pattern.compile("\\p{InCombiningDiacriticalMarks}+");
+
+    static final Object sLock = new Object();
+    static ApplicationsState sInstance;
+
+    public static ApplicationsState getInstance(Application app) {
+        synchronized (sLock) {
+            if (sInstance == null) {
+                sInstance = new ApplicationsState(app);
+            }
+            return sInstance;
+        }
+    }
+
+    final Context mContext;
+    final PackageManager mPm;
+    final IPackageManager mIpm;
+    final UserManager mUm;
+    final int mOwnerRetrieveFlags;
+    final int mRetrieveFlags;
+    PackageIntentReceiver mPackageIntentReceiver;
+
+    boolean mResumed;
+    boolean mHaveDisabledApps;
+
+    // Information about all applications.  Synchronize on mEntriesMap
+    // to protect access to these.
+    final ArrayList<Session> mSessions = new ArrayList<Session>();
+    final ArrayList<Session> mRebuildingSessions = new ArrayList<Session>();
+    final InterestingConfigChanges mInterestingConfigChanges = new InterestingConfigChanges();
+    // Map: userid => (Map: package name => AppEntry)
+    final SparseArray<HashMap<String, AppEntry>> mEntriesMap =
+            new SparseArray<HashMap<String, AppEntry>>();
+    final ArrayList<AppEntry> mAppEntries = new ArrayList<AppEntry>();
+    List<ApplicationInfo> mApplications = new ArrayList<ApplicationInfo>();
+    long mCurId = 1;
+    String mCurComputingSizePkg;
+    int mCurComputingSizeUserId;
+    boolean mSessionsChanged;
+
+    // Temporary for dispatching session callbacks.  Only touched by main thread.
+    final ArrayList<Session> mActiveSessions = new ArrayList<Session>();
+
+    final HandlerThread mThread;
+    final BackgroundHandler mBackgroundHandler;
+    final MainHandler mMainHandler = new MainHandler();
+
+    private ApplicationsState(Application app) {
+        mContext = app;
+        mPm = mContext.getPackageManager();
+        mIpm = AppGlobals.getPackageManager();
+        mUm = (UserManager) app.getSystemService(Context.USER_SERVICE);
+        for (UserHandle user : mUm.getUserProfiles()) {
+            mEntriesMap.put(user.getIdentifier(), new HashMap<String, AppEntry>());
+        }
+        mThread = new HandlerThread("ApplicationsState.Loader",
+                Process.THREAD_PRIORITY_BACKGROUND);
+        mThread.start();
+        mBackgroundHandler = new BackgroundHandler(mThread.getLooper());
+
+        // Only the owner can see all apps.
+        mOwnerRetrieveFlags = PackageManager.GET_UNINSTALLED_PACKAGES |
+                PackageManager.GET_DISABLED_COMPONENTS |
+                PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS;
+        mRetrieveFlags = PackageManager.GET_DISABLED_COMPONENTS |
+                PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS;
+
+        /**
+         * This is a trick to prevent the foreground thread from being delayed.
+         * The problem is that Dalvik monitors are initially spin locks, to keep
+         * them lightweight.  This leads to unfair contention -- Even though the
+         * background thread only holds the lock for a short amount of time, if
+         * it keeps running and locking again it can prevent the main thread from
+         * acquiring its lock for a long time...  sometimes even > 5 seconds
+         * (leading to an ANR).
+         *
+         * Dalvik will promote a monitor to a "real" lock if it detects enough
+         * contention on it.  It doesn't figure this out fast enough for us
+         * here, though, so this little trick will force it to turn into a real
+         * lock immediately.
+         */
+        synchronized (mEntriesMap) {
+            try {
+                mEntriesMap.wait(1);
+            } catch (InterruptedException e) {
+            }
+        }
+    }
+
+    public Looper getBackgroundLooper() {
+        return mThread.getLooper();
+    }
+
+    public Session newSession(Callbacks callbacks) {
+        Session s = new Session(callbacks);
+        synchronized (mEntriesMap) {
+            mSessions.add(s);
+        }
+        return s;
+    }
+
+    void doResumeIfNeededLocked() {
+        if (mResumed) {
+            return;
+        }
+        mResumed = true;
+        if (mPackageIntentReceiver == null) {
+            mPackageIntentReceiver = new PackageIntentReceiver();
+            mPackageIntentReceiver.registerReceiver();
+        }
+        mApplications = new ArrayList<ApplicationInfo>();
+        for (UserHandle user : mUm.getUserProfiles()) {
+            try {
+                // If this user is new, it needs a map created.
+                if (mEntriesMap.indexOfKey(user.getIdentifier()) < 0) {
+                    mEntriesMap.put(user.getIdentifier(), new HashMap<String, AppEntry>());
+                }
+                @SuppressWarnings("unchecked")
+                ParceledListSlice<ApplicationInfo> list =
+                        mIpm.getInstalledApplications(
+                                user.isOwner() ? mOwnerRetrieveFlags : mRetrieveFlags,
+                                user.getIdentifier());
+                mApplications.addAll(list.getList());
+            } catch (RemoteException e) {
+            }
+        }
+
+        if (mInterestingConfigChanges.applyNewConfig(mContext.getResources())) {
+            // If an interesting part of the configuration has changed, we
+            // should completely reload the app entries.
+            clearEntries();
+        } else {
+            for (int i=0; i<mAppEntries.size(); i++) {
+                mAppEntries.get(i).sizeStale = true;
+            }
+        }
+
+        mHaveDisabledApps = false;
+        for (int i=0; i<mApplications.size(); i++) {
+            final ApplicationInfo info = mApplications.get(i);
+            // Need to trim out any applications that are disabled by
+            // something different than the user.
+            if (!info.enabled) {
+                if (info.enabledSetting != PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) {
+                    mApplications.remove(i);
+                    i--;
+                    continue;
+                }
+                mHaveDisabledApps = true;
+            }
+            int userId = UserHandle.getUserId(info.uid);
+            final AppEntry entry = mEntriesMap.get(userId).get(info.packageName);
+            if (entry != null) {
+                entry.info = info;
+            }
+        }
+        if (mAppEntries.size() > mApplications.size()) {
+            // There are less apps now, some must have been uninstalled.
+            clearEntries();
+        }
+        mCurComputingSizePkg = null;
+        if (!mBackgroundHandler.hasMessages(BackgroundHandler.MSG_LOAD_ENTRIES)) {
+            mBackgroundHandler.sendEmptyMessage(BackgroundHandler.MSG_LOAD_ENTRIES);
+        }
+    }
+
+    private void clearEntries() {
+        for (int i = 0; i < mEntriesMap.size(); i++) {
+            mEntriesMap.valueAt(i).clear();
+        }
+        mAppEntries.clear();
+    }
+
+    public boolean haveDisabledApps() {
+        return mHaveDisabledApps;
+    }
+
+    void doPauseIfNeededLocked() {
+        if (!mResumed) {
+            return;
+        }
+        for (int i=0; i<mSessions.size(); i++) {
+            if (mSessions.get(i).mResumed) {
+                return;
+            }
+        }
+        doPauseLocked();
+    }
+
+    void doPauseLocked() {
+        mResumed = false;
+        if (mPackageIntentReceiver != null) {
+            mPackageIntentReceiver.unregisterReceiver();
+            mPackageIntentReceiver = null;
+        }
+    }
+
+    public AppEntry getEntry(String packageName, int userId) {
+        if (DEBUG_LOCKING) Log.v(TAG, "getEntry about to acquire lock...");
+        synchronized (mEntriesMap) {
+            AppEntry entry = mEntriesMap.get(userId).get(packageName);
+            if (entry == null) {
+                ApplicationInfo info = getAppInfoLocked(packageName, userId);
+                if (info == null) {
+                    try {
+                        info = mIpm.getApplicationInfo(packageName, 0, userId);
+                    } catch (RemoteException e) {
+                        Log.w(TAG, "getEntry couldn't reach PackageManager", e);
+                        return null;
+                    }
+                }
+                entry = getEntryLocked(info);
+            }
+            if (DEBUG_LOCKING) Log.v(TAG, "...getEntry releasing lock");
+            return entry;
+        }
+    }
+
+    private ApplicationInfo getAppInfoLocked(String pkg, int userId) {
+        for (int i = 0; i < mApplications.size(); i++) {
+            ApplicationInfo info = mApplications.get(i);
+            if (pkg.equals(info.packageName)
+                    && userId == UserHandle.getUserId(info.uid)) {
+                return info;
+            }
+        }
+        return null;
+    }
+
+    public void ensureIcon(AppEntry entry) {
+        if (entry.icon != null) {
+            return;
+        }
+        synchronized (entry) {
+            entry.ensureIconLocked(mContext, mPm);
+        }
+    }
+
+    public void requestSize(String packageName, int userId) {
+        if (DEBUG_LOCKING) Log.v(TAG, "requestSize about to acquire lock...");
+        synchronized (mEntriesMap) {
+            AppEntry entry = mEntriesMap.get(userId).get(packageName);
+            if (entry != null) {
+                mPm.getPackageSizeInfo(packageName, userId, mBackgroundHandler.mStatsObserver);
+            }
+            if (DEBUG_LOCKING) Log.v(TAG, "...requestSize releasing lock");
+        }
+    }
+
+    long sumCacheSizes() {
+        long sum = 0;
+        if (DEBUG_LOCKING) Log.v(TAG, "sumCacheSizes about to acquire lock...");
+        synchronized (mEntriesMap) {
+            if (DEBUG_LOCKING) Log.v(TAG, "-> sumCacheSizes now has lock");
+            for (int i=mAppEntries.size()-1; i>=0; i--) {
+                sum += mAppEntries.get(i).cacheSize;
+            }
+            if (DEBUG_LOCKING) Log.v(TAG, "...sumCacheSizes releasing lock");
+        }
+        return sum;
+    }
+
+    int indexOfApplicationInfoLocked(String pkgName, int userId) {
+        for (int i=mApplications.size()-1; i>=0; i--) {
+            ApplicationInfo appInfo = mApplications.get(i);
+            if (appInfo.packageName.equals(pkgName)
+                    && UserHandle.getUserId(appInfo.uid) == userId) {
+                return i;
+            }
+        }
+        return -1;
+    }
+
+    void addPackage(String pkgName, int userId) {
+        try {
+            synchronized (mEntriesMap) {
+                if (DEBUG_LOCKING) Log.v(TAG, "addPackage acquired lock");
+                if (DEBUG) Log.i(TAG, "Adding package " + pkgName);
+                if (!mResumed) {
+                    // If we are not resumed, we will do a full query the
+                    // next time we resume, so there is no reason to do work
+                    // here.
+                    if (DEBUG_LOCKING) Log.v(TAG, "addPackage release lock: not resumed");
+                    return;
+                }
+                if (indexOfApplicationInfoLocked(pkgName, userId) >= 0) {
+                    if (DEBUG) Log.i(TAG, "Package already exists!");
+                    if (DEBUG_LOCKING) Log.v(TAG, "addPackage release lock: already exists");
+                    return;
+                }
+                ApplicationInfo info = mIpm.getApplicationInfo(pkgName,
+                        userId == UserHandle.USER_OWNER ? mOwnerRetrieveFlags : mRetrieveFlags,
+                        userId);
+                if (info == null) {
+                    return;
+                }
+                if (!info.enabled) {
+                    if (info.enabledSetting
+                            != PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) {
+                        return;
+                    }
+                    mHaveDisabledApps = true;
+                }
+                mApplications.add(info);
+                if (!mBackgroundHandler.hasMessages(BackgroundHandler.MSG_LOAD_ENTRIES)) {
+                    mBackgroundHandler.sendEmptyMessage(BackgroundHandler.MSG_LOAD_ENTRIES);
+                }
+                if (!mMainHandler.hasMessages(MainHandler.MSG_PACKAGE_LIST_CHANGED)) {
+                    mMainHandler.sendEmptyMessage(MainHandler.MSG_PACKAGE_LIST_CHANGED);
+                }
+                if (DEBUG_LOCKING) Log.v(TAG, "addPackage releasing lock");
+            }
+        } catch (RemoteException e) {
+        }
+    }
+
+    public void removePackage(String pkgName, int userId) {
+        synchronized (mEntriesMap) {
+            if (DEBUG_LOCKING) Log.v(TAG, "removePackage acquired lock");
+            int idx = indexOfApplicationInfoLocked(pkgName, userId);
+            if (DEBUG) Log.i(TAG, "removePackage: " + pkgName + " @ " + idx);
+            if (idx >= 0) {
+                AppEntry entry = mEntriesMap.get(userId).get(pkgName);
+                if (DEBUG) Log.i(TAG, "removePackage: " + entry);
+                if (entry != null) {
+                    mEntriesMap.get(userId).remove(pkgName);
+                    mAppEntries.remove(entry);
+                }
+                ApplicationInfo info = mApplications.get(idx);
+                mApplications.remove(idx);
+                if (!info.enabled) {
+                    mHaveDisabledApps = false;
+                    for (int i=0; i<mApplications.size(); i++) {
+                        if (!mApplications.get(i).enabled) {
+                            mHaveDisabledApps = true;
+                            break;
+                        }
+                    }
+                }
+                if (!mMainHandler.hasMessages(MainHandler.MSG_PACKAGE_LIST_CHANGED)) {
+                    mMainHandler.sendEmptyMessage(MainHandler.MSG_PACKAGE_LIST_CHANGED);
+                }
+            }
+            if (DEBUG_LOCKING) Log.v(TAG, "removePackage releasing lock");
+        }
+    }
+
+    public void invalidatePackage(String pkgName, int userId) {
+        removePackage(pkgName, userId);
+        addPackage(pkgName, userId);
+    }
+
+    private void addUser(int userId) {
+        if (mUm.getUserProfiles().contains(new UserHandle(userId))) {
+            synchronized (mEntriesMap) {
+                mEntriesMap.put(userId, new HashMap<String, AppEntry>());
+                if (mResumed) {
+                    // If resumed, Manually pause, then cause a resume to repopulate the app list.
+                    // This is the simplest way to reload the packages so that the new user
+                    // is included.  Otherwise the list will be repopulated on next resume.
+                    doPauseLocked();
+                    doResumeIfNeededLocked();
+                }
+                if (!mMainHandler.hasMessages(MainHandler.MSG_PACKAGE_LIST_CHANGED)) {
+                    mMainHandler.sendEmptyMessage(MainHandler.MSG_PACKAGE_LIST_CHANGED);
+                }
+            }
+        }
+    }
+
+    private void removeUser(int userId) {
+        synchronized (mEntriesMap) {
+            HashMap<String, AppEntry> userMap = mEntriesMap.get(userId);
+            if (userMap != null) {
+                for (AppEntry appEntry : userMap.values()) {
+                    mAppEntries.remove(appEntry);
+                    mApplications.remove(appEntry.info);
+                }
+                mEntriesMap.remove(userId);
+                if (!mMainHandler.hasMessages(MainHandler.MSG_PACKAGE_LIST_CHANGED)) {
+                    mMainHandler.sendEmptyMessage(MainHandler.MSG_PACKAGE_LIST_CHANGED);
+                }
+            }
+        }
+    }
+
+    private AppEntry getEntryLocked(ApplicationInfo info) {
+        int userId = UserHandle.getUserId(info.uid);
+        AppEntry entry = mEntriesMap.get(userId).get(info.packageName);
+        if (DEBUG) Log.i(TAG, "Looking up entry of pkg " + info.packageName + ": " + entry);
+        if (entry == null) {
+            if (DEBUG) Log.i(TAG, "Creating AppEntry for " + info.packageName);
+            entry = new AppEntry(mContext, info, mCurId++);
+            mEntriesMap.get(userId).put(info.packageName, entry);
+            mAppEntries.add(entry);
+        } else if (entry.info != info) {
+            entry.info = info;
+        }
+        return entry;
+    }
+
+    // --------------------------------------------------------------
+
+    private long getTotalInternalSize(PackageStats ps) {
+        if (ps != null) {
+            return ps.codeSize + ps.dataSize;
+        }
+        return SIZE_INVALID;
+    }
+
+    private long getTotalExternalSize(PackageStats ps) {
+        if (ps != null) {
+            // We also include the cache size here because for non-emulated
+            // we don't automtically clean cache files.
+            return ps.externalCodeSize + ps.externalDataSize
+                    + ps.externalCacheSize
+                    + ps.externalMediaSize + ps.externalObbSize;
+        }
+        return SIZE_INVALID;
+    }
+
+    private String getSizeStr(long size) {
+        if (size >= 0) {
+            return Formatter.formatFileSize(mContext, size);
+        }
+        return null;
+    }
+
+    void rebuildActiveSessions() {
+        synchronized (mEntriesMap) {
+            if (!mSessionsChanged) {
+                return;
+            }
+            mActiveSessions.clear();
+            for (int i=0; i<mSessions.size(); i++) {
+                Session s = mSessions.get(i);
+                if (s.mResumed) {
+                    mActiveSessions.add(s);
+                }
+            }
+        }
+    }
+
+    public static String normalize(String str) {
+        String tmp = Normalizer.normalize(str, Form.NFD);
+        return REMOVE_DIACRITICALS_PATTERN.matcher(tmp)
+                .replaceAll("").toLowerCase();
+    }
+
+    public class Session {
+        final Callbacks mCallbacks;
+        boolean mResumed;
+
+        // Rebuilding of app list.  Synchronized on mRebuildSync.
+        final Object mRebuildSync = new Object();
+        boolean mRebuildRequested;
+        boolean mRebuildAsync;
+        AppFilter mRebuildFilter;
+        Comparator<AppEntry> mRebuildComparator;
+        ArrayList<AppEntry> mRebuildResult;
+        ArrayList<AppEntry> mLastAppList;
+
+        Session(Callbacks callbacks) {
+            mCallbacks = callbacks;
+        }
+
+        public void resume() {
+            if (DEBUG_LOCKING) Log.v(TAG, "resume about to acquire lock...");
+            synchronized (mEntriesMap) {
+                if (!mResumed) {
+                    mResumed = true;
+                    mSessionsChanged = true;
+                    doResumeIfNeededLocked();
+                }
+            }
+            if (DEBUG_LOCKING) Log.v(TAG, "...resume releasing lock");
+        }
+
+        public void pause() {
+            if (DEBUG_LOCKING) Log.v(TAG, "pause about to acquire lock...");
+            synchronized (mEntriesMap) {
+                if (mResumed) {
+                    mResumed = false;
+                    mSessionsChanged = true;
+                    mBackgroundHandler.removeMessages(BackgroundHandler.MSG_REBUILD_LIST, this);
+                    doPauseIfNeededLocked();
+                }
+                if (DEBUG_LOCKING) Log.v(TAG, "...pause releasing lock");
+            }
+        }
+
+        public ArrayList<AppEntry> getAllApps() {
+            synchronized (mEntriesMap) {
+                return new ArrayList<>(mAppEntries);
+            }
+        }
+
+        // Creates a new list of app entries with the given filter and comparator.
+        public ArrayList<AppEntry> rebuild(AppFilter filter, Comparator<AppEntry> comparator) {
+            synchronized (mRebuildSync) {
+                synchronized (mEntriesMap) {
+                    mRebuildingSessions.add(this);
+                    mRebuildRequested = true;
+                    mRebuildAsync = false;
+                    mRebuildFilter = filter;
+                    mRebuildComparator = comparator;
+                    mRebuildResult = null;
+                    if (!mBackgroundHandler.hasMessages(BackgroundHandler.MSG_REBUILD_LIST)) {
+                        Message msg = mBackgroundHandler.obtainMessage(
+                                BackgroundHandler.MSG_REBUILD_LIST);
+                        mBackgroundHandler.sendMessage(msg);
+                    }
+                }
+
+                // We will wait for .25s for the list to be built.
+                long waitend = SystemClock.uptimeMillis()+250;
+
+                while (mRebuildResult == null) {
+                    long now = SystemClock.uptimeMillis();
+                    if (now >= waitend) {
+                        break;
+                    }
+                    try {
+                        mRebuildSync.wait(waitend - now);
+                    } catch (InterruptedException e) {
+                    }
+                }
+
+                mRebuildAsync = true;
+
+                return mRebuildResult;
+            }
+        }
+
+        void handleRebuildList() {
+            AppFilter filter;
+            Comparator<AppEntry> comparator;
+            synchronized (mRebuildSync) {
+                if (!mRebuildRequested) {
+                    return;
+                }
+
+                filter = mRebuildFilter;
+                comparator = mRebuildComparator;
+                mRebuildRequested = false;
+                mRebuildFilter = null;
+                mRebuildComparator = null;
+            }
+
+            Process.setThreadPriority(Process.THREAD_PRIORITY_FOREGROUND);
+
+            if (filter != null) {
+                filter.init();
+            }
+
+            List<AppEntry> apps;
+            synchronized (mEntriesMap) {
+                apps = new ArrayList<>(mAppEntries);
+            }
+
+            ArrayList<AppEntry> filteredApps = new ArrayList<AppEntry>();
+            if (DEBUG) Log.i(TAG, "Rebuilding...");
+            for (int i=0; i<apps.size(); i++) {
+                AppEntry entry = apps.get(i);
+                if (filter == null || filter.filterApp(entry)) {
+                    synchronized (mEntriesMap) {
+                        if (DEBUG_LOCKING) Log.v(TAG, "rebuild acquired lock");
+                        entry.ensureLabel(mContext);
+                        if (DEBUG) Log.i(TAG, "Using " + entry.info.packageName + ": " + entry);
+                        filteredApps.add(entry);
+                        if (DEBUG_LOCKING) Log.v(TAG, "rebuild releasing lock");
+                    }
+                }
+            }
+
+            Collections.sort(filteredApps, comparator);
+
+            synchronized (mRebuildSync) {
+                if (!mRebuildRequested) {
+                    mLastAppList = filteredApps;
+                    if (!mRebuildAsync) {
+                        mRebuildResult = filteredApps;
+                        mRebuildSync.notifyAll();
+                    } else {
+                        if (!mMainHandler.hasMessages(MainHandler.MSG_REBUILD_COMPLETE, this)) {
+                            Message msg = mMainHandler.obtainMessage(
+                                    MainHandler.MSG_REBUILD_COMPLETE, this);
+                            mMainHandler.sendMessage(msg);
+                        }
+                    }
+                }
+            }
+
+            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
+        }
+
+        public void release() {
+            pause();
+            synchronized (mEntriesMap) {
+                mSessions.remove(this);
+            }
+        }
+    }
+
+    class MainHandler extends Handler {
+        static final int MSG_REBUILD_COMPLETE = 1;
+        static final int MSG_PACKAGE_LIST_CHANGED = 2;
+        static final int MSG_PACKAGE_ICON_CHANGED = 3;
+        static final int MSG_PACKAGE_SIZE_CHANGED = 4;
+        static final int MSG_ALL_SIZES_COMPUTED = 5;
+        static final int MSG_RUNNING_STATE_CHANGED = 6;
+        static final int MSG_LAUNCHER_INFO_CHANGED = 7;
+        static final int MSG_LOAD_ENTRIES_COMPLETE = 8;
+
+        @Override
+        public void handleMessage(Message msg) {
+            rebuildActiveSessions();
+            switch (msg.what) {
+                case MSG_REBUILD_COMPLETE: {
+                    Session s = (Session)msg.obj;
+                    if (mActiveSessions.contains(s)) {
+                        s.mCallbacks.onRebuildComplete(s.mLastAppList);
+                    }
+                } break;
+                case MSG_PACKAGE_LIST_CHANGED: {
+                    for (int i=0; i<mActiveSessions.size(); i++) {
+                        mActiveSessions.get(i).mCallbacks.onPackageListChanged();
+                    }
+                } break;
+                case MSG_PACKAGE_ICON_CHANGED: {
+                    for (int i=0; i<mActiveSessions.size(); i++) {
+                        mActiveSessions.get(i).mCallbacks.onPackageIconChanged();
+                    }
+                } break;
+                case MSG_PACKAGE_SIZE_CHANGED: {
+                    for (int i=0; i<mActiveSessions.size(); i++) {
+                        mActiveSessions.get(i).mCallbacks.onPackageSizeChanged(
+                                (String)msg.obj);
+                    }
+                } break;
+                case MSG_ALL_SIZES_COMPUTED: {
+                    for (int i=0; i<mActiveSessions.size(); i++) {
+                        mActiveSessions.get(i).mCallbacks.onAllSizesComputed();
+                    }
+                } break;
+                case MSG_RUNNING_STATE_CHANGED: {
+                    for (int i=0; i<mActiveSessions.size(); i++) {
+                        mActiveSessions.get(i).mCallbacks.onRunningStateChanged(
+                                msg.arg1 != 0);
+                    }
+                } break;
+                case MSG_LAUNCHER_INFO_CHANGED: {
+                    for (int i=0; i<mActiveSessions.size(); i++) {
+                        mActiveSessions.get(i).mCallbacks.onLauncherInfoChanged();
+                    }
+                } break;
+                case MSG_LOAD_ENTRIES_COMPLETE: {
+                    for (int i=0; i<mActiveSessions.size(); i++) {
+                        mActiveSessions.get(i).mCallbacks.onLoadEntriesCompleted();
+                    }
+                } break;
+            }
+        }
+    }
+
+    private class BackgroundHandler extends Handler {
+        static final int MSG_REBUILD_LIST = 1;
+        static final int MSG_LOAD_ENTRIES = 2;
+        static final int MSG_LOAD_ICONS = 3;
+        static final int MSG_LOAD_SIZES = 4;
+        static final int MSG_LOAD_LAUNCHER = 5;
+
+        boolean mRunning;
+
+        BackgroundHandler(Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            // Always try rebuilding list first thing, if needed.
+            ArrayList<Session> rebuildingSessions = null;
+            synchronized (mEntriesMap) {
+                if (mRebuildingSessions.size() > 0) {
+                    rebuildingSessions = new ArrayList<Session>(mRebuildingSessions);
+                    mRebuildingSessions.clear();
+                }
+            }
+            if (rebuildingSessions != null) {
+                for (int i=0; i<rebuildingSessions.size(); i++) {
+                    rebuildingSessions.get(i).handleRebuildList();
+                }
+            }
+
+            switch (msg.what) {
+                case MSG_REBUILD_LIST: {
+                } break;
+                case MSG_LOAD_ENTRIES: {
+                    int numDone = 0;
+                    synchronized (mEntriesMap) {
+                        if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_ENTRIES acquired lock");
+                        for (int i = 0; i < mApplications.size() && numDone < 6; i++) {
+                            if (!mRunning) {
+                                mRunning = true;
+                                Message m = mMainHandler.obtainMessage(
+                                        MainHandler.MSG_RUNNING_STATE_CHANGED, 1);
+                                mMainHandler.sendMessage(m);
+                            }
+                            ApplicationInfo info = mApplications.get(i);
+                            int userId = UserHandle.getUserId(info.uid);
+                            if (mEntriesMap.get(userId).get(info.packageName) == null) {
+                                numDone++;
+                                getEntryLocked(info);
+                            }
+                        }
+                        if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_ENTRIES releasing lock");
+                    }
+
+                    if (numDone >= 6) {
+                        sendEmptyMessage(MSG_LOAD_ENTRIES);
+                    } else {
+                        if (!mMainHandler.hasMessages(MainHandler.MSG_LOAD_ENTRIES_COMPLETE)) {
+                            mMainHandler.sendEmptyMessage(MainHandler.MSG_LOAD_ENTRIES_COMPLETE);
+                        }
+                        sendEmptyMessage(MSG_LOAD_LAUNCHER);
+                    }
+                } break;
+                case MSG_LOAD_LAUNCHER: {
+                    Intent launchIntent = new Intent(Intent.ACTION_MAIN, null)
+                            .addCategory(Intent.CATEGORY_LAUNCHER);
+
+                    for (int i = 0; i < mEntriesMap.size(); i++) {
+                        int userId = mEntriesMap.keyAt(i);
+                        List<ResolveInfo> intents = mPm.queryIntentActivitiesAsUser(launchIntent,
+                                PackageManager.GET_DISABLED_COMPONENTS, userId);
+                        synchronized (mEntriesMap) {
+                            if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_LAUNCHER acquired lock");
+                            HashMap<String, AppEntry> userEntries = mEntriesMap.valueAt(i);
+                            final int N = intents.size();
+                            for (int j = 0; j < N; j++) {
+                                String packageName = intents.get(j).activityInfo.packageName;
+                                AppEntry entry = userEntries.get(packageName);
+                                if (entry != null) {
+                                    entry.hasLauncherEntry = true;
+                                } else {
+                                    Log.w(TAG, "Cannot find pkg: " + packageName
+                                            + " on user " + userId);
+                                }
+                            }
+                            if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_LAUNCHER releasing lock");
+                        }
+                    }
+
+                    if (!mMainHandler.hasMessages(MainHandler.MSG_LAUNCHER_INFO_CHANGED)) {
+                        mMainHandler.sendEmptyMessage(MainHandler.MSG_LAUNCHER_INFO_CHANGED);
+                    }
+                    sendEmptyMessage(MSG_LOAD_ICONS);
+                } break;
+                case MSG_LOAD_ICONS: {
+                    int numDone = 0;
+                    synchronized (mEntriesMap) {
+                        if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_ICONS acquired lock");
+                        for (int i=0; i<mAppEntries.size() && numDone<2; i++) {
+                            AppEntry entry = mAppEntries.get(i);
+                            if (entry.icon == null || !entry.mounted) {
+                                synchronized (entry) {
+                                    if (entry.ensureIconLocked(mContext, mPm)) {
+                                        if (!mRunning) {
+                                            mRunning = true;
+                                            Message m = mMainHandler.obtainMessage(
+                                                    MainHandler.MSG_RUNNING_STATE_CHANGED, 1);
+                                            mMainHandler.sendMessage(m);
+                                        }
+                                        numDone++;
+                                    }
+                                }
+                            }
+                        }
+                        if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_ICONS releasing lock");
+                    }
+                    if (numDone > 0) {
+                        if (!mMainHandler.hasMessages(MainHandler.MSG_PACKAGE_ICON_CHANGED)) {
+                            mMainHandler.sendEmptyMessage(MainHandler.MSG_PACKAGE_ICON_CHANGED);
+                        }
+                    }
+                    if (numDone >= 2) {
+                        sendEmptyMessage(MSG_LOAD_ICONS);
+                    } else {
+                        sendEmptyMessage(MSG_LOAD_SIZES);
+                    }
+                } break;
+                case MSG_LOAD_SIZES: {
+                    synchronized (mEntriesMap) {
+                        if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_SIZES acquired lock");
+                        if (mCurComputingSizePkg != null) {
+                            if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_SIZES releasing: currently computing");
+                            return;
+                        }
+
+                        long now = SystemClock.uptimeMillis();
+                        for (int i=0; i<mAppEntries.size(); i++) {
+                            AppEntry entry = mAppEntries.get(i);
+                            if (entry.size == SIZE_UNKNOWN || entry.sizeStale) {
+                                if (entry.sizeLoadStart == 0 ||
+                                        (entry.sizeLoadStart < (now-20*1000))) {
+                                    if (!mRunning) {
+                                        mRunning = true;
+                                        Message m = mMainHandler.obtainMessage(
+                                                MainHandler.MSG_RUNNING_STATE_CHANGED, 1);
+                                        mMainHandler.sendMessage(m);
+                                    }
+                                    entry.sizeLoadStart = now;
+                                    mCurComputingSizePkg = entry.info.packageName;
+                                    mCurComputingSizeUserId = UserHandle.getUserId(entry.info.uid);
+                                    mPm.getPackageSizeInfo(mCurComputingSizePkg,
+                                            mCurComputingSizeUserId, mStatsObserver);
+                                }
+                                if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_SIZES releasing: now computing");
+                                return;
+                            }
+                        }
+                        if (!mMainHandler.hasMessages(MainHandler.MSG_ALL_SIZES_COMPUTED)) {
+                            mMainHandler.sendEmptyMessage(MainHandler.MSG_ALL_SIZES_COMPUTED);
+                            mRunning = false;
+                            Message m = mMainHandler.obtainMessage(
+                                    MainHandler.MSG_RUNNING_STATE_CHANGED, 0);
+                            mMainHandler.sendMessage(m);
+                        }
+                        if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_SIZES releasing lock");
+                    }
+                } break;
+            }
+        }
+
+        final IPackageStatsObserver.Stub mStatsObserver = new IPackageStatsObserver.Stub() {
+            public void onGetStatsCompleted(PackageStats stats, boolean succeeded) {
+                boolean sizeChanged = false;
+                synchronized (mEntriesMap) {
+                    if (DEBUG_LOCKING) Log.v(TAG, "onGetStatsCompleted acquired lock");
+                    HashMap<String, AppEntry> userMap = mEntriesMap.get(stats.userHandle);
+                    if (userMap == null) {
+                        // The user must have been removed.
+                        return;
+                    }
+                    AppEntry entry = userMap.get(stats.packageName);
+                    if (entry != null) {
+                        synchronized (entry) {
+                            entry.sizeStale = false;
+                            entry.sizeLoadStart = 0;
+                            long externalCodeSize = stats.externalCodeSize
+                                    + stats.externalObbSize;
+                            long externalDataSize = stats.externalDataSize
+                                    + stats.externalMediaSize;
+                            long newSize = externalCodeSize + externalDataSize
+                                    + getTotalInternalSize(stats);
+                            if (entry.size != newSize ||
+                                    entry.cacheSize != stats.cacheSize ||
+                                    entry.codeSize != stats.codeSize ||
+                                    entry.dataSize != stats.dataSize ||
+                                    entry.externalCodeSize != externalCodeSize ||
+                                    entry.externalDataSize != externalDataSize ||
+                                    entry.externalCacheSize != stats.externalCacheSize) {
+                                entry.size = newSize;
+                                entry.cacheSize = stats.cacheSize;
+                                entry.codeSize = stats.codeSize;
+                                entry.dataSize = stats.dataSize;
+                                entry.externalCodeSize = externalCodeSize;
+                                entry.externalDataSize = externalDataSize;
+                                entry.externalCacheSize = stats.externalCacheSize;
+                                entry.sizeStr = getSizeStr(entry.size);
+                                entry.internalSize = getTotalInternalSize(stats);
+                                entry.internalSizeStr = getSizeStr(entry.internalSize);
+                                entry.externalSize = getTotalExternalSize(stats);
+                                entry.externalSizeStr = getSizeStr(entry.externalSize);
+                                if (DEBUG) Log.i(TAG, "Set size of " + entry.label + " " + entry
+                                        + ": " + entry.sizeStr);
+                                sizeChanged = true;
+                            }
+                        }
+                        if (sizeChanged) {
+                            Message msg = mMainHandler.obtainMessage(
+                                    MainHandler.MSG_PACKAGE_SIZE_CHANGED, stats.packageName);
+                            mMainHandler.sendMessage(msg);
+                        }
+                    }
+                    if (mCurComputingSizePkg != null
+                            && (mCurComputingSizePkg.equals(stats.packageName)
+                            && mCurComputingSizeUserId == stats.userHandle)) {
+                        mCurComputingSizePkg = null;
+                        sendEmptyMessage(MSG_LOAD_SIZES);
+                    }
+                    if (DEBUG_LOCKING) Log.v(TAG, "onGetStatsCompleted releasing lock");
+                }
+            }
+        };
+    }
+
+    /**
+     * Receives notifications when applications are added/removed.
+     */
+    private class PackageIntentReceiver extends BroadcastReceiver {
+        void registerReceiver() {
+            IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
+            filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+            filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+            filter.addDataScheme("package");
+            mContext.registerReceiver(this, filter);
+            // Register for events related to sdcard installation.
+            IntentFilter sdFilter = new IntentFilter();
+            sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
+            sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
+            mContext.registerReceiver(this, sdFilter);
+            // Register for events related to user creation/deletion.
+            IntentFilter userFilter = new IntentFilter();
+            userFilter.addAction(Intent.ACTION_USER_ADDED);
+            userFilter.addAction(Intent.ACTION_USER_REMOVED);
+            mContext.registerReceiver(this, userFilter);
+        }
+        void unregisterReceiver() {
+            mContext.unregisterReceiver(this);
+        }
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String actionStr = intent.getAction();
+            if (Intent.ACTION_PACKAGE_ADDED.equals(actionStr)) {
+                Uri data = intent.getData();
+                String pkgName = data.getEncodedSchemeSpecificPart();
+                for (int i = 0; i < mEntriesMap.size(); i++) {
+                    addPackage(pkgName, mEntriesMap.keyAt(i));
+                }
+            } else if (Intent.ACTION_PACKAGE_REMOVED.equals(actionStr)) {
+                Uri data = intent.getData();
+                String pkgName = data.getEncodedSchemeSpecificPart();
+                for (int i = 0; i < mEntriesMap.size(); i++) {
+                    removePackage(pkgName, mEntriesMap.keyAt(i));
+                }
+            } else if (Intent.ACTION_PACKAGE_CHANGED.equals(actionStr)) {
+                Uri data = intent.getData();
+                String pkgName = data.getEncodedSchemeSpecificPart();
+                for (int i = 0; i < mEntriesMap.size(); i++) {
+                    invalidatePackage(pkgName, mEntriesMap.keyAt(i));
+                }
+            } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(actionStr) ||
+                    Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(actionStr)) {
+                // When applications become available or unavailable (perhaps because
+                // the SD card was inserted or ejected) we need to refresh the
+                // AppInfo with new label, icon and size information as appropriate
+                // given the newfound (un)availability of the application.
+                // A simple way to do that is to treat the refresh as a package
+                // removal followed by a package addition.
+                String pkgList[] = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
+                if (pkgList == null || pkgList.length == 0) {
+                    // Ignore
+                    return;
+                }
+                boolean avail = Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(actionStr);
+                if (avail) {
+                    for (String pkgName : pkgList) {
+                        for (int i = 0; i < mEntriesMap.size(); i++) {
+                            invalidatePackage(pkgName, mEntriesMap.keyAt(i));
+                        }
+                    }
+                }
+            } else if (Intent.ACTION_USER_ADDED.equals(actionStr)) {
+                addUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL));
+            } else if (Intent.ACTION_USER_REMOVED.equals(actionStr)) {
+                removeUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL));
+            }
+        }
+    }
+
+    public interface Callbacks {
+        void onRunningStateChanged(boolean running);
+        void onPackageListChanged();
+        void onRebuildComplete(ArrayList<AppEntry> apps);
+        void onPackageIconChanged();
+        void onPackageSizeChanged(String packageName);
+        void onAllSizesComputed();
+        void onLauncherInfoChanged();
+        void onLoadEntriesCompleted();
+    }
+
+    public static class SizeInfo {
+        public long cacheSize;
+        public long codeSize;
+        public long dataSize;
+        public long externalCodeSize;
+        public long externalDataSize;
+
+        // This is the part of externalDataSize that is in the cache
+        // section of external storage.  Note that we don't just combine
+        // this with cacheSize because currently the platform can't
+        // automatically trim this data when needed, so it is something
+        // the user may need to manage.  The externalDataSize also includes
+        // this value, since what this is here is really the part of
+        // externalDataSize that we can just consider to be "cache" files
+        // for purposes of cleaning them up in the app details UI.
+        public long externalCacheSize;
+    }
+
+    public static class AppEntry extends SizeInfo {
+        public final File apkFile;
+        public final long id;
+        public String label;
+        public long size;
+        public long internalSize;
+        public long externalSize;
+
+        public boolean mounted;
+
+        public boolean hasLauncherEntry;
+
+        public String getNormalizedLabel() {
+            if (normalizedLabel != null) {
+                return normalizedLabel;
+            }
+            normalizedLabel = normalize(label);
+            return normalizedLabel;
+        }
+
+        // Need to synchronize on 'this' for the following.
+        public ApplicationInfo info;
+        public Drawable icon;
+        public String sizeStr;
+        public String internalSizeStr;
+        public String externalSizeStr;
+        public boolean sizeStale;
+        public long sizeLoadStart;
+
+        public String normalizedLabel;
+
+        // A location where extra info can be placed to be used by custom filters.
+        public Object extraInfo;
+
+        AppEntry(Context context, ApplicationInfo info, long id) {
+            apkFile = new File(info.sourceDir);
+            this.id = id;
+            this.info = info;
+            this.size = SIZE_UNKNOWN;
+            this.sizeStale = true;
+            ensureLabel(context);
+        }
+
+        public void ensureLabel(Context context) {
+            if (this.label == null || !this.mounted) {
+                if (!this.apkFile.exists()) {
+                    this.mounted = false;
+                    this.label = info.packageName;
+                } else {
+                    this.mounted = true;
+                    CharSequence label = info.loadLabel(context.getPackageManager());
+                    this.label = label != null ? label.toString() : info.packageName;
+                }
+            }
+        }
+
+        boolean ensureIconLocked(Context context, PackageManager pm) {
+            if (this.icon == null) {
+                if (this.apkFile.exists()) {
+                    this.icon = getBadgedIcon(pm);
+                    return true;
+                } else {
+                    this.mounted = false;
+                    this.icon = context.getDrawable(
+                            com.android.internal.R.drawable.sym_app_on_sd_unavailable_icon);
+                }
+            } else if (!this.mounted) {
+                // If the app wasn't mounted but is now mounted, reload
+                // its icon.
+                if (this.apkFile.exists()) {
+                    this.mounted = true;
+                    this.icon = getBadgedIcon(pm);
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        private Drawable getBadgedIcon(PackageManager pm) {
+            // Do badging ourself so that it comes from the user of the app not the current user.
+            return pm.getUserBadgedIcon(pm.loadUnbadgedItemIcon(info, info),
+                    new UserHandle(UserHandle.getUserId(info.uid)));
+        }
+
+        public String getVersion(Context context) {
+            try {
+                return context.getPackageManager().getPackageInfo(info.packageName, 0).versionName;
+            } catch (PackageManager.NameNotFoundException e) {
+                return "";
+            }
+        }
+    }
+
+    public static final Comparator<AppEntry> ALPHA_COMPARATOR = new Comparator<AppEntry>() {
+        private final Collator sCollator = Collator.getInstance();
+        @Override
+        public int compare(AppEntry object1, AppEntry object2) {
+            return sCollator.compare(object1.label, object2.label);
+        }
+    };
+
+    public static final Comparator<AppEntry> SIZE_COMPARATOR
+            = new Comparator<AppEntry>() {
+        private final Collator sCollator = Collator.getInstance();
+        @Override
+        public int compare(AppEntry object1, AppEntry object2) {
+            if (object1.size < object2.size) return 1;
+            if (object1.size > object2.size) return -1;
+            return sCollator.compare(object1.label, object2.label);
+        }
+    };
+
+    public static final Comparator<AppEntry> INTERNAL_SIZE_COMPARATOR
+            = new Comparator<AppEntry>() {
+        private final Collator sCollator = Collator.getInstance();
+        @Override
+        public int compare(AppEntry object1, AppEntry object2) {
+            if (object1.internalSize < object2.internalSize) return 1;
+            if (object1.internalSize > object2.internalSize) return -1;
+            return sCollator.compare(object1.label, object2.label);
+        }
+    };
+
+    public static final Comparator<AppEntry> EXTERNAL_SIZE_COMPARATOR
+            = new Comparator<AppEntry>() {
+        private final Collator sCollator = Collator.getInstance();
+        @Override
+        public int compare(AppEntry object1, AppEntry object2) {
+            if (object1.externalSize < object2.externalSize) return 1;
+            if (object1.externalSize > object2.externalSize) return -1;
+            return sCollator.compare(object1.label, object2.label);
+        }
+    };
+
+    public interface AppFilter {
+        void init();
+        boolean filterApp(AppEntry info);
+    }
+
+    public static final AppFilter FILTER_PERSONAL = new AppFilter() {
+        private int mCurrentUser;
+
+        public void init() {
+            mCurrentUser = ActivityManager.getCurrentUser();
+        }
+
+        @Override
+        public boolean filterApp(AppEntry entry) {
+            return UserHandle.getUserId(entry.info.uid) == mCurrentUser;
+        }
+    };
+
+    public static final AppFilter FILTER_WORK = new AppFilter() {
+        private int mCurrentUser;
+
+        public void init() {
+            mCurrentUser = ActivityManager.getCurrentUser();
+        }
+
+        @Override
+        public boolean filterApp(AppEntry entry) {
+            return UserHandle.getUserId(entry.info.uid) != mCurrentUser;
+        }
+    };
+
+    public static final AppFilter FILTER_DOWNLOADED_AND_LAUNCHER = new AppFilter() {
+        public void init() {
+        }
+
+        @Override
+        public boolean filterApp(AppEntry entry) {
+            if ((entry.info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
+                return true;
+            } else if ((entry.info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
+                return true;
+            } else if (entry.hasLauncherEntry) {
+                return true;
+            }
+            return false;
+        }
+    };
+
+    public static final AppFilter FILTER_THIRD_PARTY = new AppFilter() {
+        public void init() {
+        }
+
+        @Override
+        public boolean filterApp(AppEntry entry) {
+            if ((entry.info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
+                return true;
+            } else if ((entry.info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
+                return true;
+            }
+            return false;
+        }
+    };
+
+    public static final AppFilter FILTER_DISABLED = new AppFilter() {
+        public void init() {
+        }
+
+        @Override
+        public boolean filterApp(AppEntry entry) {
+            return !entry.info.enabled;
+        }
+    };
+
+    public static final AppFilter FILTER_ALL_ENABLED = new AppFilter() {
+        public void init() {
+        }
+
+        @Override
+        public boolean filterApp(AppEntry entry) {
+            return entry.info.enabled;
+        }
+    };
+
+    public static final AppFilter FILTER_EVERYTHING = new AppFilter() {
+        public void init() {
+        }
+
+        @Override
+        public boolean filterApp(AppEntry entry) {
+            return true;
+        }
+    };
+
+    public static final AppFilter FILTER_WITH_DOMAIN_URLS = new AppFilter() {
+        public void init() {
+        }
+
+        @Override
+        public boolean filterApp(AppEntry entry) {
+            return (entry.info.privateFlags & ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS) != 0;
+        }
+    };
+
+    public static class VolumeFilter implements AppFilter {
+        private final String mVolumeUuid;
+
+        public VolumeFilter(String volumeUuid) {
+            mVolumeUuid = volumeUuid;
+        }
+
+        @Override
+        public void init() {
+        }
+
+        @Override
+        public boolean filterApp(AppEntry info) {
+            return Objects.equals(info.info.volumeUuid, mVolumeUuid);
+        }
+    }
+
+    public static class CompoundFilter implements AppFilter {
+        private final AppFilter mFirstFilter;
+        private final AppFilter mSecondFilter;
+
+        public CompoundFilter(AppFilter first, AppFilter second) {
+            mFirstFilter = first;
+            mSecondFilter = second;
+        }
+
+        @Override
+        public void init() {
+            mFirstFilter.init();
+            mSecondFilter.init();
+        }
+
+        @Override
+        public boolean filterApp(AppEntry info) {
+            return mFirstFilter.filterApp(info) && mSecondFilter.filterApp(info);
+        }
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/InterestingConfigChanges.java b/packages/SettingsLib/src/com/android/settingslib/applications/InterestingConfigChanges.java
new file mode 100644
index 0000000..d34dd89
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/InterestingConfigChanges.java
@@ -0,0 +1,37 @@
+/*
+ * 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.settingslib.applications;
+
+import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+
+public class InterestingConfigChanges {
+    private final Configuration mLastConfiguration = new Configuration();
+    private int mLastDensity;
+
+    public boolean applyNewConfig(Resources res) {
+        int configChanges = mLastConfiguration.updateFrom(res.getConfiguration());
+        boolean densityChanged = mLastDensity != res.getDisplayMetrics().densityDpi;
+        if (densityChanged || (configChanges&(ActivityInfo.CONFIG_LOCALE
+                |ActivityInfo.CONFIG_UI_MODE|ActivityInfo.CONFIG_SCREEN_LAYOUT)) != 0) {
+            mLastDensity = res.getDisplayMetrics().densityDpi;
+            return true;
+        }
+        return false;
+    }
+}
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 7cb4956..14c8262 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -911,7 +911,7 @@
     <string name="monitoring_description_device_owned">Your device is managed by <xliff:g id="organization">%1$s</xliff:g>.\n\nYour administrator can monitor and manage settings, corporate access, apps, data associated with your device, and your device\'s location information. For more information, contact your administrator.</string>
 
     <!-- Monitoring dialog VPN text [CHAR LIMIT=400] -->
-    <string name="monitoring_description_vpn">You gave an app permission to set up a VPN connection.\n\nThis app can monitor your device and network activity, including emails, apps and websites.</string>
+    <string name="monitoring_description_vpn">You gave an app permission to set up a VPN connection.\n\nThis app can monitor your device and network activity, including emails, apps, and websites.</string>
 
     <!-- Monitoring dialog VPN with device owner text [CHAR LIMIT=400] -->
     <string name="monitoring_description_vpn_device_owned">Your device is managed by <xliff:g id="organization">%1$s</xliff:g>.\n\nYour administrator can monitor and manage settings, corporate access, apps, data associated with your device, and your device\'s location information.\n\nYou\'re connected to a VPN, which can monitor your network activity, including emails, apps, and websites.\n\nFor more information, contact your administrator.</string>
@@ -923,16 +923,16 @@
     <string name="legacy_vpn_name">VPN</string>
 
     <!-- Monitoring dialog text for single app (no profile or device owner) [CHAR LIMIT=400] -->
-    <string name="monitoring_description_app">You\'re connected to <xliff:g id="application">%1$s</xliff:g>, which can monitor your network activity including emails, apps and websites.</string>
+    <string name="monitoring_description_app">You\'re connected to <xliff:g id="application">%1$s</xliff:g>, which can monitor your network activity including emails, apps, and websites.</string>
 
     <!-- Monitoring dialog text for single app (inside personal profile) [CHAR LIMIT=400] -->
-    <string name="monitoring_description_app_personal">You\'re connected to <xliff:g id="application">%1$s</xliff:g>, which can monitor your personal network activity, including emails, apps and websites.</string>
+    <string name="monitoring_description_app_personal">You\'re connected to <xliff:g id="application">%1$s</xliff:g>, which can monitor your personal network activity, including emails, apps, and websites.</string>
 
     <!-- Monitoring dialog text for single app (inside work profile) [CHAR LIMIT=400] -->
-    <string name="monitoring_description_app_work">Your work profile is managed by <xliff:g id="organization">%1$s</xliff:g>. It is connected to <xliff:g id="application">%2$s</xliff:g>, which can monitor your work network activity, including emails, apps and websites.\n\nFor more information, contact your administrator.</string>
+    <string name="monitoring_description_app_work">Your work profile is managed by <xliff:g id="organization">%1$s</xliff:g>. It is connected to <xliff:g id="application">%2$s</xliff:g>, which can monitor your work network activity, including emails, apps, and websites.\n\nFor more information, contact your administrator.</string>
 
     <!-- Monitoring dialog text for multiple apps (in personal and work profiles) [CHAR LIMIT=400] -->
-    <string name="monitoring_description_app_personal_work">Your work profile is managed by <xliff:g id="organization">%1$s</xliff:g>. It is connected to <xliff:g id="application_work">%2$s</xliff:g>, which can monitor your work network activity, including emails, apps and websites.\n\nYou\'re also connected to <xliff:g id="application_personal">%3$s</xliff:g>, which can monitor your personal network activity.</string>
+    <string name="monitoring_description_app_personal_work">Your work profile is managed by <xliff:g id="organization">%1$s</xliff:g>. It is connected to <xliff:g id="application_work">%2$s</xliff:g>, which can monitor your work network activity, including emails, apps, and websites.\n\nYou\'re also connected to <xliff:g id="application_personal">%3$s</xliff:g>, which can monitor your personal network activity.</string>
 
     <!-- Monitoring dialog text for single app (with device owner) [CHAR LIMIT=400] -->
     <string name="monitoring_description_vpn_app_device_owned">Your device is managed by <xliff:g id="organization">%1$s</xliff:g>.\n\nYour administrator can monitor and manage settings, corporate access, apps, data associated with your device, and your device\'s location information.\n\nYou\'re connected to <xliff:g id="application">%2$s</xliff:g>, which can monitor your network activity, including emails, apps, and websites.\n\nFor more information, contact your administrator.</string>
@@ -1087,4 +1087,10 @@
     <!-- Alarm template for far alarms [CHAR LIMIT=25] -->
     <string name="alarm_template_far">on <xliff:g id="when" example="Fri 7:00 AM">%1$s</xliff:g></string>
 
+    <!-- Accessibility label for Quick Settings detail screens [CHAR LIMIT=NONE] -->
+    <string name="accessibility_quick_settings_detail">Quick Settings, <xliff:g id="title" example="Wi-Fi">%s</xliff:g>.</string>
+
+    <!-- Accessibility label for hotspot icon [CHAR LIMIT=NONE] -->
+    <string name="accessibility_status_bar_hotspot">Hotspot</string>
+
 </resources>
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index cd4f299..25e3d10 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -111,6 +111,8 @@
         mDetailDoneButton.setOnClickListener(new OnClickListener() {
             @Override
             public void onClick(View v) {
+                announceForAccessibility(
+                        mContext.getString(R.string.accessibility_desc_quick_settings));
                 closeDetail();
             }
         });
@@ -392,6 +394,9 @@
             mDetail.bringToFront();
             mDetailContent.addView(r.detailView);
             MetricsLogger.visible(mContext, detailAdapter.getMetricsCategory());
+            announceForAccessibility(mContext.getString(
+                    R.string.accessibility_quick_settings_detail,
+                    mContext.getString(detailAdapter.getTitle())));
             setDetailRecord(r);
             listener = mHideGridContentWhenDone;
             if (r instanceof TileRecord) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/DismissViewButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/DismissViewButton.java
index 00665f4..a323684 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/DismissViewButton.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/DismissViewButton.java
@@ -48,15 +48,14 @@
     public DismissViewButton(Context context, AttributeSet attrs, int defStyleAttr,
             int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
-        mAnimatedDismissDrawable = (AnimatedVectorDrawable) getContext().getResources().getDrawable(
+        mAnimatedDismissDrawable = (AnimatedVectorDrawable) getContext().getDrawable(
                 R.drawable.dismiss_all_shape_animation).mutate();
         mAnimatedDismissDrawable.setCallback(this);
         mAnimatedDismissDrawable.setBounds(0,
                 0,
                 mAnimatedDismissDrawable.getIntrinsicWidth(),
                 mAnimatedDismissDrawable.getIntrinsicHeight());
-        mStaticDismissDrawable = getContext().getResources().getDrawable(
-                R.drawable.dismiss_all_shape);
+        mStaticDismissDrawable = getContext().getDrawable(R.drawable.dismiss_all_shape);
         mStaticDismissDrawable.setBounds(0,
                 0,
                 mStaticDismissDrawable.getIntrinsicWidth(),
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index 0872e06..6a6266e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -162,7 +162,8 @@
         mCast.addCallback(mCastCallback);
 
         // hotspot
-        mService.setIcon(SLOT_HOTSPOT, R.drawable.stat_sys_hotspot, 0, null);
+        mService.setIcon(SLOT_HOTSPOT, R.drawable.stat_sys_hotspot, 0,
+                mContext.getString(R.string.accessibility_status_bar_hotspot));
         mService.setIconVisibility(SLOT_HOTSPOT, mHotspot.isHotspotEnabled());
         mHotspot.addCallback(mHotspotCallback);
 
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
index 310a64c..29bea4d 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
@@ -386,18 +386,8 @@
                         }
                     }
                 } else {
-                    if (mAutomute && !row.ss.muteSupported) {
-                        final boolean vmute = row.ss.level == 0;
-                        mController.setStreamVolume(stream, vmute ? row.lastAudibleLevel : 0);
-                    } else {
-                        final boolean mute = !row.ss.muted;
-                        mController.setStreamMute(stream, mute);
-                        if (mAutomute) {
-                            if (!mute && row.ss.level == 0) {
-                                mController.setStreamVolume(stream, 1);
-                            }
-                        }
-                    }
+                    final boolean vmute = row.ss.level == 0;
+                    mController.setStreamVolume(stream, vmute ? row.lastAudibleLevel : 0);
                 }
                 row.userAttempt = 0;  // reset the grace period, slider should update immediately
             }
@@ -589,6 +579,9 @@
         if (ss.level > 0) {
             row.lastAudibleLevel = ss.level;
         }
+        if (ss.level == row.requestedLevel) {
+            row.requestedLevel = -1;
+        }
         final boolean isRingStream = row.stream == AudioManager.STREAM_RING;
         final boolean isSystemStream = row.stream == AudioManager.STREAM_SYSTEM;
         final boolean isAlarmStream = row.stream == AudioManager.STREAM_ALARM;
@@ -664,7 +657,10 @@
         row.icon.setContentDescription(ss.name);
 
         // update slider
-        updateVolumeRowSliderH(row, zenMuted);
+        final boolean enableSlider = !zenMuted;
+        final int vlevel = row.ss.muted && (isRingVibrate || !isRingStream && !zenMuted) ? 0
+                : row.ss.level;
+        updateVolumeRowSliderH(row, enableSlider, vlevel);
     }
 
     private void updateVolumeRowSliderTintH(VolumeRow row, boolean isActive) {
@@ -676,8 +672,8 @@
         row.slider.setThumbTintList(tint);
     }
 
-    private void updateVolumeRowSliderH(VolumeRow row, boolean zenMuted) {
-        row.slider.setEnabled(!zenMuted);
+    private void updateVolumeRowSliderH(VolumeRow row, boolean enable, int vlevel) {
+        row.slider.setEnabled(enable);
         updateVolumeRowSliderTintH(row, row.stream == mActiveStream);
         if (row.tracking) {
             return;  // don't update if user is sliding
@@ -694,7 +690,6 @@
                     row.userAttempt + USER_ATTEMPT_GRACE_PERIOD);
             return;  // don't update if visible and in grace period
         }
-        final int vlevel = row.ss.muted ? 0 : row.ss.level;
         if (vlevel == level) {
             if (mShowing && rowVisible) {
                 return;  // don't clamp if visible
@@ -1018,7 +1013,7 @@
         private StreamState ss;
         private long userAttempt;  // last user-driven slider change
         private boolean tracking;  // tracking slider touch
-        private int requestedLevel;
+        private int requestedLevel = -1;  // pending user-requested level via progress changed
         private int iconRes;
         private int iconMuteRes;
         private boolean important;
diff --git a/services/core/java/com/android/server/AnyMotionDetector.java b/services/core/java/com/android/server/AnyMotionDetector.java
new file mode 100644
index 0000000..6390bcd
--- /dev/null
+++ b/services/core/java/com/android/server/AnyMotionDetector.java
@@ -0,0 +1,438 @@
+/*
+ * 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;
+
+import android.app.AlarmManager;
+import android.content.BroadcastReceiver;
+import android.content.Intent;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.os.Handler;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.SystemClock;
+import android.util.Slog;
+
+import java.lang.Float;
+
+/**
+ * Determines if the device has been set upon a stationary object.
+ */
+public class AnyMotionDetector {
+    interface DeviceIdleCallback {
+        public void onAnyMotionResult(int result);
+    }
+
+    private static final String TAG = "AnyMotionDetector";
+
+    private static final boolean DEBUG = false;
+
+    /** Stationary status is unknown due to insufficient orientation measurements. */
+    public static final int RESULT_UNKNOWN = -1;
+
+    /** Device is stationary, e.g. still on a table. */
+    public static final int RESULT_STATIONARY = 0;
+
+    /** Device has been moved. */
+    public static final int RESULT_MOVED = 1;
+
+    /** Orientation measurements are being performed or are planned. */
+    private static final int STATE_INACTIVE = 0;
+
+    /** No orientation measurements are being performed or are planned. */
+    private static final int STATE_ACTIVE = 1;
+
+    /** Current measurement state. */
+    private int mState;
+
+    /** Threshold angle in degrees beyond which the device is considered moving. */
+    private final float THRESHOLD_ANGLE = 2f;
+
+    /** Threshold energy above which the device is considered moving. */
+    private final float THRESHOLD_ENERGY = 5f;
+
+    /** The duration of the accelerometer orientation measurement. */
+    private static final long ORIENTATION_MEASUREMENT_DURATION_MILLIS = 2500;
+
+    /** The maximum duration we will collect accelerometer data. */
+    private static final long ACCELEROMETER_DATA_TIMEOUT_MILLIS = 3000;
+
+    /** The interval between accelerometer orientation measurements. */
+    private static final long ORIENTATION_MEASUREMENT_INTERVAL_MILLIS = 5000;
+
+    /**
+     * The duration in milliseconds after which an orientation measurement is considered
+     * too stale to be used.
+     */
+    private static final int STALE_MEASUREMENT_TIMEOUT_MILLIS = 2 * 60 * 1000;
+
+    /** The accelerometer sampling interval. */
+    private static final int SAMPLING_INTERVAL_MILLIS = 40;
+
+    private AlarmManager mAlarmManager;
+    private final Handler mHandler;
+    private Intent mAlarmIntent;
+    private final Object mLock = new Object();
+    private Sensor mAccelSensor;
+    private SensorManager mSensorManager;
+    private PowerManager.WakeLock mWakeLock;
+
+    /** The time when detection was last performed. */
+    private long mDetectionStartTime;
+
+    /** The minimum number of samples required to detect AnyMotion. */
+    private int mNumSufficientSamples;
+
+    /** True if an orientation measurement is in progress. */
+    private boolean mMeasurementInProgress;
+
+    /** The most recent gravity vector. */
+    private Vector3 mCurrentGravityVector = null;
+
+    /** The second most recent gravity vector. */
+    private Vector3 mPreviousGravityVector = null;
+
+    /** Running sum of squared errors. */
+    private RunningSignalStats mRunningStats;
+
+    private DeviceIdleCallback mCallback = null;
+
+    public AnyMotionDetector(AlarmManager am, PowerManager pm, Handler handler, SensorManager sm,
+            DeviceIdleCallback callback) {
+        if (DEBUG) Slog.d(TAG, "AnyMotionDetector instantiated.");
+        mAlarmManager = am;
+        mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
+        mHandler = handler;
+        mSensorManager = sm;
+        mAccelSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
+        mMeasurementInProgress = false;
+        mState = STATE_INACTIVE;
+        mCallback = callback;
+        mRunningStats = new RunningSignalStats();
+        mNumSufficientSamples = (int) Math.ceil(
+                ((double)ORIENTATION_MEASUREMENT_DURATION_MILLIS / SAMPLING_INTERVAL_MILLIS));
+        if (DEBUG) Slog.d(TAG, "mNumSufficientSamples = " + mNumSufficientSamples);
+    }
+
+    /*
+     * Acquire accel data until we determine AnyMotion status.
+     */
+    public void checkForAnyMotion() {
+      if (DEBUG) Slog.d(TAG, "checkForAnyMotion(). mState = " + mState);
+        if (mState != STATE_ACTIVE) {
+            mState = STATE_ACTIVE;
+            if (DEBUG) Slog.d(TAG, "Moved from STATE_INACTIVE to STATE_ACTIVE.");
+            mCurrentGravityVector = null;
+            mPreviousGravityVector = null;
+            startOrientationMeasurement();
+        }
+    }
+
+    private void startOrientationMeasurement() {
+        if (DEBUG) Slog.d(TAG, "startOrientationMeasurement: mMeasurementInProgress=" +
+            mMeasurementInProgress + ", (mAccelSensor != null)=" + (mAccelSensor != null));
+
+        if (!mMeasurementInProgress && mAccelSensor != null) {
+            if (mSensorManager.registerListener(mListener, mAccelSensor,
+                    SAMPLING_INTERVAL_MILLIS * 1000)) {
+                mWakeLock.acquire();
+                mMeasurementInProgress = true;
+                mDetectionStartTime = SystemClock.elapsedRealtime();
+                mRunningStats.reset();
+            }
+
+            Message msg = Message.obtain(mHandler, mMeasurementTimeout);
+            msg.setAsynchronous(true);
+            mHandler.sendMessageDelayed(msg, ACCELEROMETER_DATA_TIMEOUT_MILLIS);
+        }
+    }
+
+    private int stopOrientationMeasurementLocked() {
+        if (DEBUG) Slog.d(TAG, "stopOrientationMeasurement. mMeasurementInProgress=" +
+                mMeasurementInProgress);
+        int status = RESULT_UNKNOWN;
+        if (mMeasurementInProgress) {
+            mSensorManager.unregisterListener(mListener);
+            mHandler.removeCallbacks(mMeasurementTimeout);
+            if (mWakeLock.isHeld()) {
+                mWakeLock.release();
+            }
+            long detectionEndTime = SystemClock.elapsedRealtime();
+            mMeasurementInProgress = false;
+            mPreviousGravityVector = mCurrentGravityVector;
+            mCurrentGravityVector = mRunningStats.getRunningAverage();
+            if (DEBUG) {
+                Slog.d(TAG, "mRunningStats = " + mRunningStats.toString());
+                String currentGravityVectorString = (mCurrentGravityVector == null) ?
+                        "null" : mCurrentGravityVector.toString();
+                String previousGravityVectorString = (mPreviousGravityVector == null) ?
+                        "null" : mPreviousGravityVector.toString();
+                Slog.d(TAG, "mCurrentGravityVector = " + currentGravityVectorString);
+                Slog.d(TAG, "mPreviousGravityVector = " + previousGravityVectorString);
+            }
+            mRunningStats.reset();
+            status = getStationaryStatus();
+            if (DEBUG) Slog.d(TAG, "getStationaryStatus() returned " + status);
+            if (status != RESULT_UNKNOWN) {
+                if (DEBUG) Slog.d(TAG, "Moved from STATE_ACTIVE to STATE_INACTIVE. status = " +
+                        status);
+                mState = STATE_INACTIVE;
+            } else {
+                /*
+                 * Unknown due to insufficient measurements. Schedule another orientation
+                 * measurement.
+                 */
+                if (DEBUG) Slog.d(TAG, "stopOrientationMeasurementLocked(): another measurement" +
+                        " scheduled in " + ORIENTATION_MEASUREMENT_INTERVAL_MILLIS +
+                        " milliseconds.");
+                Message msg = Message.obtain(mHandler, mSensorRestart);
+                msg.setAsynchronous(true);
+                mHandler.sendMessageDelayed(msg, ORIENTATION_MEASUREMENT_INTERVAL_MILLIS);
+            }
+        }
+        return status;
+    }
+
+    /*
+     * Updates mStatus to the current AnyMotion status.
+     */
+    public int getStationaryStatus() {
+        if ((mPreviousGravityVector == null) || (mCurrentGravityVector == null)) {
+            return RESULT_UNKNOWN;
+        }
+        Vector3 previousGravityVectorNormalized = mPreviousGravityVector.normalized();
+        Vector3 currentGravityVectorNormalized = mCurrentGravityVector.normalized();
+        float angle = previousGravityVectorNormalized.angleBetween(currentGravityVectorNormalized);
+        if (DEBUG) Slog.d(TAG, "getStationaryStatus: angle = " + angle);
+        if ((angle < THRESHOLD_ANGLE) && (mRunningStats.getEnergy() < THRESHOLD_ENERGY)) {
+            return RESULT_STATIONARY;
+        } else if (Float.isNaN(angle)) {
+          /**
+           * Floating point rounding errors have caused the angle calcuation's dot product to 
+           * exceed 1.0. In such case, we report RESULT_MOVED to prevent devices from rapidly
+           * retrying this measurement.
+           */
+            return RESULT_MOVED;
+        }
+        long diffTime = mCurrentGravityVector.timeMillisSinceBoot -
+                mPreviousGravityVector.timeMillisSinceBoot;
+        if (diffTime > STALE_MEASUREMENT_TIMEOUT_MILLIS) {
+            if (DEBUG) Slog.d(TAG, "getStationaryStatus: mPreviousGravityVector is too stale at " +
+                    diffTime + " ms ago. Returning RESULT_UNKNOWN.");
+            return RESULT_UNKNOWN;
+        }
+        return RESULT_MOVED;
+    }
+
+    private final SensorEventListener mListener = new SensorEventListener() {
+        @Override
+        public void onSensorChanged(SensorEvent event) {
+            int status = RESULT_UNKNOWN;
+            synchronized (mLock) {
+                Vector3 accelDatum = new Vector3(SystemClock.elapsedRealtime(), event.values[0],
+                        event.values[1], event.values[2]);
+                mRunningStats.accumulate(accelDatum);
+
+                // If we have enough samples, stop accelerometer data acquisition.
+                if (mRunningStats.getSampleCount() >= mNumSufficientSamples) {
+                    status = stopOrientationMeasurementLocked();
+                }
+            }
+            if (status != RESULT_UNKNOWN) {
+                mCallback.onAnyMotionResult(status);
+            }
+        }
+
+        @Override
+        public void onAccuracyChanged(Sensor sensor, int accuracy) {
+        }
+    };
+
+    private final Runnable mSensorRestart = new Runnable() {
+        @Override
+        public void run() {
+            synchronized (mLock) {
+                startOrientationMeasurement();
+            }
+        }
+    };
+
+    private final Runnable mMeasurementTimeout = new Runnable() {
+      @Override
+      public void run() {
+          int status = RESULT_UNKNOWN;
+          synchronized (mLock) {
+              if (DEBUG) Slog.i(TAG, "mMeasurementTimeout. Failed to collect sufficient accel " +
+                      "data within " + ACCELEROMETER_DATA_TIMEOUT_MILLIS + " ms. Stopping " +
+                      "orientation measurement.");
+              status = stopOrientationMeasurementLocked();
+          }
+          if (status != RESULT_UNKNOWN) {
+              mCallback.onAnyMotionResult(status);
+          }
+      }
+  };
+
+    /**
+     * A timestamped three dimensional vector and some vector operations.
+     */
+    private static class Vector3 {
+        public long timeMillisSinceBoot;
+        public float x;
+        public float y;
+        public float z;
+
+        public Vector3(long timeMillisSinceBoot, float x, float y, float z) {
+            this.timeMillisSinceBoot = timeMillisSinceBoot;
+            this.x = x;
+            this.y = y;
+            this.z = z;
+        }
+
+        private float norm() {
+            return (float) Math.sqrt(dotProduct(this));
+        }
+
+        private Vector3 normalized() {
+            float mag = norm();
+            return new Vector3(timeMillisSinceBoot, x / mag, y / mag, z / mag);
+        }
+
+        /**
+         * Returns the angle between this 3D vector and another given 3D vector.
+         * Assumes both have already been normalized.
+         *
+         * @param other The other Vector3 vector.
+         * @return angle between this vector and the other given one.
+         */
+        public float angleBetween(Vector3 other) {
+            double degrees = Math.toDegrees(Math.acos(this.dotProduct(other)));
+            float returnValue = (float) degrees;
+            Slog.d(TAG, "angleBetween: this = " + this.toString() +
+                    ", other = " + other.toString());
+            Slog.d(TAG, "    degrees = " + degrees + ", returnValue = " + returnValue);
+            return returnValue;
+        }
+
+        @Override
+        public String toString() {
+            String msg = "";
+            msg += "timeMillisSinceBoot=" + timeMillisSinceBoot;
+            msg += " | x=" + x;
+            msg += ", y=" + y;
+            msg += ", z=" + z;
+            return msg;
+        }
+
+        public float dotProduct(Vector3 v) {
+            return x * v.x + y * v.y + z * v.z;
+        }
+
+        public Vector3 times(float val) {
+            return new Vector3(timeMillisSinceBoot, x * val, y * val, z * val);
+        }
+
+        public Vector3 plus(Vector3 v) {
+            return new Vector3(v.timeMillisSinceBoot, x + v.x, y + v.y, z + v.z);
+        }
+
+        public Vector3 minus(Vector3 v) {
+            return new Vector3(v.timeMillisSinceBoot, x - v.x, y - v.y, z - v.z);
+        }
+    }
+
+    /**
+     * Maintains running statistics on the signal revelant to AnyMotion detection, including:
+     * <ul>
+     *   <li>running average.
+     *   <li>running sum-of-squared-errors as the energy of the signal derivative.
+     * <ul>
+     */
+    private static class RunningSignalStats {
+        Vector3 previousVector;
+        Vector3 currentVector;
+        Vector3 runningSum;
+        float energy;
+        int sampleCount;
+
+        public RunningSignalStats() {
+            reset();
+        }
+
+        public void reset() {
+            previousVector = null;
+            currentVector = null;
+            runningSum = new Vector3(0, 0, 0, 0);
+            energy = 0;
+            sampleCount = 0;
+        }
+
+        /**
+         * Apply a 3D vector v as the next element in the running SSE.
+         */
+        public void accumulate(Vector3 v) {
+            if (v == null) {
+                if (DEBUG) Slog.i(TAG, "Cannot accumulate a null vector.");
+                return;
+            }
+            sampleCount++;
+            runningSum = runningSum.plus(v);
+            previousVector = currentVector;
+            currentVector = v;
+            if (previousVector != null) {
+                Vector3 dv = currentVector.minus(previousVector);
+                float incrementalEnergy = dv.x * dv.x + dv.y * dv.y + dv.z * dv.z;
+                energy += incrementalEnergy;
+                if (DEBUG) Slog.i(TAG, "Accumulated vector " + currentVector.toString() +
+                        ", runningSum = " + runningSum.toString() +
+                        ", incrementalEnergy = " + incrementalEnergy +
+                        ", energy = " + energy);
+            }
+        }
+
+        public Vector3 getRunningAverage() {
+            if (sampleCount > 0) {
+              return runningSum.times((float)(1.0f / sampleCount));
+            }
+            return null;
+        }
+
+        public float getEnergy() {
+            return energy;
+        }
+
+        public int getSampleCount() {
+            return sampleCount;
+        }
+
+        @Override
+        public String toString() {
+            String msg = "";
+            String currentVectorString = (currentVector == null) ?
+                "null" : currentVector.toString();
+            String previousVectorString = (previousVector == null) ?
+                "null" : previousVector.toString();
+            msg += "previousVector = " + previousVectorString;
+            msg += ", currentVector = " + currentVectorString;
+            msg += ", sampleCount = " + sampleCount;
+            msg += ", energy = " + energy;
+            return msg;
+        }
+    }
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java
index e9759c3..4c7b523 100644
--- a/services/core/java/com/android/server/DeviceIdleController.java
+++ b/services/core/java/com/android/server/DeviceIdleController.java
@@ -85,10 +85,12 @@
 /**
  * Keeps track of device idleness and drives low power mode based on that.
  */
-public class DeviceIdleController extends SystemService {
+public class DeviceIdleController extends SystemService
+        implements AnyMotionDetector.DeviceIdleCallback {
     private static final String TAG = "DeviceIdleController";
 
     private static final boolean DEBUG = false;
+
     private static final boolean COMPRESS_TIME = false;
 
     public static final String SERVICE_NAME = "deviceidle";
@@ -96,6 +98,9 @@
     private static final String ACTION_STEP_IDLE_STATE =
             "com.android.server.device_idle.STEP_IDLE_STATE";
 
+    private static final String ACTION_ENTER_INACTIVE_STATE =
+        "com.android.server.device_idle.ENTER_INACTIVE_STATE";
+
     // TODO: These need to be moved to system settings.
 
     /**
@@ -104,26 +109,40 @@
      * immediately after going inactive just because we don't want to be continually running
      * the significant motion sensor whenever the screen is off.
      */
+
     private static final long DEFAULT_INACTIVE_TIMEOUT = !COMPRESS_TIME ? 30*60*1000L
             : 3 * 60 * 1000L;
+
+    /**
+     * If we don't receive a callback from AnyMotion in this amount of time, we will change from
+     * STATE_SENSING to STATE_INACTIVE, and any AnyMotion callbacks while not in STATE_SENSING will
+     * be ignored.
+     */
+    private static final long DEFAULT_SENSING_TIMEOUT = !DEBUG ? 5 * 60 * 1000L : 60 * 1000L;
+
     /**
      * This is the time, after seeing motion, that we wait after becoming inactive from
      * that until we start looking for motion again.
      */
     private static final long DEFAULT_MOTION_INACTIVE_TIMEOUT = !COMPRESS_TIME ? 10*60*1000L
             : 60 * 1000L;
+
     /**
      * This is the time, after the inactive timeout elapses, that we will wait looking
      * for significant motion until we truly consider the device to be idle.
      */
+
     private static final long DEFAULT_IDLE_AFTER_INACTIVE_TIMEOUT = !COMPRESS_TIME ? 30*60*1000L
             : 3 * 60 * 1000L;
+
     /**
      * This is the initial time, after being idle, that we will allow ourself to be back
      * in the IDLE_PENDING state allowing the system to run normally until we return to idle.
      */
+
     private static final long DEFAULT_IDLE_PENDING_TIMEOUT = !COMPRESS_TIME ? 5*60*1000L
             : 30 * 1000L;
+
     /**
      * Maximum pending idle timeout (time spent running) we will be allowed to use.
      */
@@ -138,8 +157,10 @@
      * This is the initial time that we want to sit in the idle state before waking up
      * again to return to pending idle and allowing normal work to run.
      */
+
     private static final long DEFAULT_IDLE_TIMEOUT = !COMPRESS_TIME ? 60*60*1000L
             : 6 * 60 * 1000L;
+
     /**
      * Maximum idle duration we will be allowed to use.
      */
@@ -168,9 +189,11 @@
     private DisplayManager mDisplayManager;
     private SensorManager mSensorManager;
     private Sensor mSigMotionSensor;
+    private PendingIntent mSensingAlarmIntent;
     private PendingIntent mAlarmIntent;
     private Intent mIdleIntent;
     private Display mCurDisplay;
+    private AnyMotionDetector mAnyMotionDetector;
     private boolean mIdleDisabled;
     private boolean mScreenOn;
     private boolean mCharging;
@@ -182,15 +205,18 @@
     private static final int STATE_INACTIVE = 1;
     /** Device is past the initial inactive period, and waiting for the next idle period. */
     private static final int STATE_IDLE_PENDING = 2;
+    /** Device is currently sensing motion. */
+    private static final int STATE_SENSING = 3;
     /** Device is in the idle state, trying to stay asleep as much as possible. */
-    private static final int STATE_IDLE = 3;
+    private static final int STATE_IDLE = 4;
     /** Device is in the idle state, but temporarily out of idle to do regular maintenance. */
-    private static final int STATE_IDLE_MAINTENANCE = 4;
+    private static final int STATE_IDLE_MAINTENANCE = 5;
     private static String stateToString(int state) {
         switch (state) {
             case STATE_ACTIVE: return "ACTIVE";
             case STATE_INACTIVE: return "INACTIVE";
             case STATE_IDLE_PENDING: return "IDLE_PENDING";
+            case STATE_SENSING: return "SENSING";
             case STATE_IDLE: return "IDLE";
             case STATE_IDLE_MAINTENANCE: return "IDLE_MAINTENANCE";
             default: return Integer.toString(state);
@@ -247,6 +273,10 @@
                 synchronized (DeviceIdleController.this) {
                     stepIdleStateLocked();
                 }
+            } else if (ACTION_ENTER_INACTIVE_STATE.equals(intent.getAction())) {
+                synchronized (DeviceIdleController.this) {
+                    enterInactiveStateLocked();
+                }
             }
         }
     };
@@ -276,6 +306,24 @@
         }
     };
 
+    @Override
+    public void onAnyMotionResult(int result) {
+        if (DEBUG) Slog.d(TAG, "onAnyMotionResult(" + result + ")");
+        if (mState == STATE_SENSING) {
+            if (result == AnyMotionDetector.RESULT_STATIONARY) {
+                if (DEBUG) Slog.d(TAG, "RESULT_STATIONARY received.");
+                synchronized (this) {
+                    stepIdleStateLocked();
+                }
+            } else if (result == AnyMotionDetector.RESULT_MOVED) {
+                if (DEBUG) Slog.d(TAG, "RESULT_MOVED received.");
+                synchronized (this) {
+                    enterInactiveStateLocked();
+                }
+            }
+        }
+    }
+
     static final int MSG_WRITE_CONFIG = 1;
     static final int MSG_REPORT_IDLE_ON = 2;
     static final int MSG_REPORT_IDLE_OFF = 3;
@@ -288,6 +336,7 @@
         }
 
         @Override public void handleMessage(Message msg) {
+            if (DEBUG) Slog.d(TAG, "handleMessage(" + msg.what + ")");
             switch (msg.what) {
                 case MSG_WRITE_CONFIG: {
                     handleWriteConfigFile();
@@ -452,12 +501,21 @@
                         Context.DISPLAY_SERVICE);
                 mSensorManager = (SensorManager) getContext().getSystemService(Context.SENSOR_SERVICE);
                 mSigMotionSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_SIGNIFICANT_MOTION);
+                mAnyMotionDetector = new AnyMotionDetector(
+                        mAlarmManager,
+                        (PowerManager) getContext().getSystemService(Context.POWER_SERVICE),
+                        mHandler, mSensorManager, this);
 
                 Intent intent = new Intent(ACTION_STEP_IDLE_STATE)
                         .setPackage("android")
                         .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
                 mAlarmIntent = PendingIntent.getBroadcast(getContext(), 0, intent, 0);
 
+                Intent intentSensing = new Intent(ACTION_STEP_IDLE_STATE)
+                        .setPackage("android")
+                        .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+                mSensingAlarmIntent = PendingIntent.getBroadcast(getContext(), 0, intentSensing, 0);
+
                 mIdleIntent = new Intent(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
                 mIdleIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
 
@@ -613,6 +671,7 @@
         // because if there is anything shown we are going to be updating it at some
         // frequency so can't be allowed to go into deep sleeps.
         boolean screenOn = mCurDisplay.getState() != Display.STATE_OFF;;
+        if (DEBUG) Slog.d(TAG, "updateDisplayLocked: screenOn=" + screenOn);
         if (!screenOn && mScreenOn) {
             mScreenOn = false;
             becomeInactiveIfAppropriateLocked();
@@ -623,6 +682,7 @@
     }
 
     void updateChargingLocked(boolean charging) {
+        if (DEBUG) Slog.i(TAG, "updateChargingLocked: charging=" + charging);
         if (!charging && mCharging) {
             mCharging = false;
             becomeInactiveIfAppropriateLocked();
@@ -639,6 +699,7 @@
     }
 
     void becomeActiveLocked(String reason) {
+        if (DEBUG) Slog.i(TAG, "becomeActiveLocked, reason = " + reason);
         if (mState != STATE_ACTIVE) {
             EventLogTags.writeDeviceIdle(STATE_ACTIVE, reason);
             scheduleReportActiveLocked(false);
@@ -652,10 +713,12 @@
     }
 
     void becomeInactiveIfAppropriateLocked() {
+        if (DEBUG) Slog.d(TAG, "becomeInactiveIfAppropriateLocked()");
         if (!mScreenOn && !mCharging && !mIdleDisabled && mState == STATE_ACTIVE) {
             // Screen has turned off; we are now going to become inactive and start
             // waiting to see if we will ultimately go idle.
             mState = STATE_INACTIVE;
+            if (DEBUG) Slog.d(TAG, "Moved from STATE_ACTIVE to STATE_INACTIVE");
             mNextIdlePendingDelay = 0;
             mNextIdleDelay = 0;
             scheduleAlarmLocked(mInactiveTimeout, false);
@@ -663,7 +726,17 @@
         }
     }
 
+    /**
+     * This is called when we've failed to receive a callback from AnyMotionDetector
+     * within the DEFAULT_SENSING_TIMEOUT, to return to STATE_INACTIVE.
+     */
+    void enterInactiveStateLocked() {
+        mInactiveTimeout = DEFAULT_INACTIVE_TIMEOUT;
+        becomeInactiveIfAppropriateLocked();
+    }
+
     void stepIdleStateLocked() {
+        if (DEBUG) Slog.d(TAG, "stepIdleStateLocked: mState=" + mState);
         EventLogTags.writeDeviceIdleStep();
 
         final long now = SystemClock.elapsedRealtime();
@@ -685,26 +758,34 @@
                 mNextIdlePendingDelay = DEFAULT_IDLE_PENDING_TIMEOUT;
                 mNextIdleDelay = DEFAULT_IDLE_TIMEOUT;
                 mState = STATE_IDLE_PENDING;
+                if (DEBUG) Slog.d(TAG, "Moved from STATE_INACTIVE to STATE_IDLE_PENDING.");
                 EventLogTags.writeDeviceIdle(mState, "step");
                 break;
             case STATE_IDLE_PENDING:
+                mState = STATE_SENSING;
+                if (DEBUG) Slog.d(TAG, "Moved from STATE_IDLE_PENDING to STATE_SENSING.");
+                scheduleSensingAlarmLocked(DEFAULT_SENSING_TIMEOUT);
+                mAnyMotionDetector.checkForAnyMotion();
+                break;
+            case STATE_SENSING:
+                cancelSensingAlarmLocked();
             case STATE_IDLE_MAINTENANCE:
-                // We have been waiting to become idle, and now it is time!  This is the
-                // only case where we want to use a wakeup alarm, because we do want to
-                // drag the device out of its sleep state in this case to do the next
-                // scheduled work.
                 scheduleAlarmLocked(mNextIdleDelay, true);
-                mNextIdleDelay = (long)(mNextIdleDelay*DEFAULT_IDLE_FACTOR);
+                if (DEBUG) Slog.d(TAG, "Moved to STATE_IDLE. Next alarm in " + mNextIdleDelay +
+                        " ms.");
+                mNextIdleDelay = (long)(mNextIdleDelay * DEFAULT_IDLE_FACTOR);
+                if (DEBUG) Slog.d(TAG, "Setting mNextIdleDelay = " + mNextIdleDelay);
                 if (mNextIdleDelay > DEFAULT_MAX_IDLE_TIMEOUT) {
                     mNextIdleDelay = DEFAULT_MAX_IDLE_TIMEOUT;
                 }
                 mState = STATE_IDLE;
-                EventLogTags.writeDeviceIdle(mState, "step");
                 mHandler.sendEmptyMessage(MSG_REPORT_IDLE_ON);
                 break;
             case STATE_IDLE:
                 // We have been idling long enough, now it is time to do some work.
                 scheduleAlarmLocked(mNextIdlePendingDelay, false);
+                if (DEBUG) Slog.d(TAG, "Moved from STATE_IDLE to STATE_IDLE_MAINTENANCE. " +
+                        "Next alarm in " + mNextIdlePendingDelay + " ms.");
                 mNextIdlePendingDelay = (long)(mNextIdlePendingDelay*DEFAULT_IDLE_PENDING_FACTOR);
                 if (mNextIdlePendingDelay > DEFAULT_MAX_IDLE_PENDING_TIMEOUT) {
                     mNextIdlePendingDelay = DEFAULT_MAX_IDLE_PENDING_TIMEOUT;
@@ -717,6 +798,7 @@
     }
 
     void significantMotionLocked() {
+        if (DEBUG) Slog.d(TAG, "significantMotionLocked()");
         // When the sensor goes off, its trigger is automatically removed.
         mSigMotionActive = false;
         // The device is not yet active, so we want to go back to the pending idle
@@ -732,6 +814,7 @@
     }
 
     void startMonitoringSignificantMotion() {
+        if (DEBUG) Slog.d(TAG, "startMonitoringSignificantMotion()");
         if (mSigMotionSensor != null && !mSigMotionActive) {
             mSensorManager.requestTriggerSensor(mSigMotionListener, mSigMotionSensor);
             mSigMotionActive = true;
@@ -739,6 +822,7 @@
     }
 
     void stopMonitoringSignificantMotion() {
+        if (DEBUG) Slog.d(TAG, "stopMonitoringSignificantMotion()");
         if (mSigMotionActive) {
             mSensorManager.cancelTriggerSensor(mSigMotionListener, mSigMotionSensor);
             mSigMotionActive = false;
@@ -752,7 +836,13 @@
         }
     }
 
+    void cancelSensingAlarmLocked() {
+        if (DEBUG) Slog.d(TAG, "cancelSensingAlarmLocked()");
+        mAlarmManager.cancel(mSensingAlarmIntent);
+    }
+
     void scheduleAlarmLocked(long delay, boolean idleUntil) {
+        if (DEBUG) Slog.d(TAG, "scheduleAlarmLocked(" + delay + ", " + idleUntil + ")");
         if (mSigMotionSensor == null) {
             // If there is no significant motion sensor on this device, then we won't schedule
             // alarms, because we can't determine if the device is not moving.  This effectively
@@ -770,6 +860,13 @@
         }
     }
 
+    void scheduleSensingAlarmLocked(long delay) {
+      if (DEBUG) Slog.d(TAG, "scheduleSensingAlarmLocked(" + delay + ")");
+      mNextAlarmTime = SystemClock.elapsedRealtime() + delay;
+      mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+          mNextAlarmTime, mSensingAlarmIntent);
+    }
+
     private void updateWhitelistAppIdsLocked() {
         mPowerSaveWhitelistAppIds.clear();
         for (int i=0; i<mPowerSaveWhitelistApps.size(); i++) {
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index bc93268..95f1852 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -92,7 +92,7 @@
         IPhoneStateListener callback;
         IOnSubscriptionsChangedListener onSubscriptionsChangedListenerCallback;
 
-        int callerUid;
+        int callerUserId;
 
         int events;
 
@@ -100,6 +100,8 @@
 
         int phoneId = SubscriptionManager.INVALID_PHONE_INDEX;
 
+        boolean canReadPhoneState;
+
         boolean matchPhoneStateListenerEvent(int events) {
             return (callback != null) && ((events & this.events) != 0);
         }
@@ -114,8 +116,9 @@
                     + " callback=" + callback
                     + " onSubscriptionsChangedListenererCallback="
                                             + onSubscriptionsChangedListenerCallback
-                    + " callerUid=" + callerUid + " subId=" + subId + " phoneId=" + phoneId
-                    + " events=" + Integer.toHexString(events) + "}";
+                    + " callerUserId=" + callerUserId + " subId=" + subId + " phoneId=" + phoneId
+                    + " events=" + Integer.toHexString(events)
+                    + " canReadPhoneState=" + canReadPhoneState + "}";
         }
     }
 
@@ -190,13 +193,15 @@
     private PreciseDataConnectionState mPreciseDataConnectionState =
                 new PreciseDataConnectionState();
 
-    static final int PHONE_STATE_PERMISSION_MASK =
+    static final int ENFORCE_PHONE_STATE_PERMISSION_MASK =
                 PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR |
+                PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR |
+                PhoneStateListener.LISTEN_VOLTE_STATE;
+
+    static final int CHECK_PHONE_STATE_PERMISSION_MASK =
                 PhoneStateListener.LISTEN_CALL_STATE |
                 PhoneStateListener.LISTEN_DATA_ACTIVITY |
-                PhoneStateListener.LISTEN_DATA_CONNECTION_STATE |
-                PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR |
-                PhoneStateListener.LISTEN_VOLTE_STATE;;
+                PhoneStateListener.LISTEN_DATA_CONNECTION_STATE;
 
     static final int PRECISE_PHONE_STATE_PERMISSION_MASK =
                 PhoneStateListener.LISTEN_PRECISE_CALL_STATE |
@@ -348,11 +353,10 @@
     @Override
     public void addOnSubscriptionsChangedListener(String callingPackage,
             IOnSubscriptionsChangedListener callback) {
-        int callerUid = UserHandle.getCallingUserId();
-        int myUid = UserHandle.myUserId();
+        int callerUserId = UserHandle.getCallingUserId();
         if (VDBG) {
-            log("listen oscl: E pkg=" + callingPackage + " myUid=" + myUid
-                + " callerUid="  + callerUid + " callback=" + callback
+            log("listen oscl: E pkg=" + callingPackage + " myUserId=" + UserHandle.myUserId()
+                + " callerUserId="  + callerUserId + " callback=" + callback
                 + " callback.asBinder=" + callback.asBinder());
         }
 
@@ -364,7 +368,7 @@
             return;
         }
 
-        Record r = null;
+        final Record r;
 
         synchronized (mRecords) {
             // register
@@ -385,8 +389,9 @@
 
             r.onSubscriptionsChangedListenerCallback = callback;
             r.callingPackage = callingPackage;
-            r.callerUid = callerUid;
+            r.callerUserId = callerUserId;
             r.events = 0;
+            r.canReadPhoneState = true; // permission has been enforced above
             if (DBG) {
                 log("listen oscl:  Register r=" + r);
             }
@@ -454,19 +459,18 @@
 
     private void listen(String callingPackage, IPhoneStateListener callback, int events,
             boolean notifyNow, int subId) {
-        int callerUid = UserHandle.getCallingUserId();
-        int myUid = UserHandle.myUserId();
+        int callerUserId = UserHandle.getCallingUserId();
         if (VDBG) {
             log("listen: E pkg=" + callingPackage + " events=0x" + Integer.toHexString(events)
-                + " notifyNow=" + notifyNow + " subId=" + subId + " myUid=" + myUid
-                + " callerUid=" + callerUid);
+                + " notifyNow=" + notifyNow + " subId=" + subId + " myUserId="
+                + UserHandle.myUserId() + " callerUserId=" + callerUserId);
         }
 
         if (events != PhoneStateListener.LISTEN_NONE) {
             /* Checks permission and throws Security exception */
             checkListenerPermission(events);
 
-            if ((events & PHONE_STATE_PERMISSION_MASK) != 0) {
+            if ((events & ENFORCE_PHONE_STATE_PERMISSION_MASK) != 0) {
                 if (mAppOps.noteOp(AppOpsManager.OP_READ_PHONE_STATE, Binder.getCallingUid(),
                         callingPackage) != AppOpsManager.MODE_ALLOWED) {
                     return;
@@ -475,7 +479,7 @@
 
             synchronized (mRecords) {
                 // register
-                Record r = null;
+                Record r;
                 find_and_add: {
                     IBinder b = callback.asBinder();
                     final int N = mRecords.size();
@@ -493,7 +497,10 @@
 
                 r.callback = callback;
                 r.callingPackage = callingPackage;
-                r.callerUid = callerUid;
+                r.callerUserId = callerUserId;
+                boolean isPhoneStateEvent = (events & (CHECK_PHONE_STATE_PERMISSION_MASK
+                        | ENFORCE_PHONE_STATE_PERMISSION_MASK)) != 0;
+                r.canReadPhoneState = isPhoneStateEvent && canReadPhoneState(callingPackage);
                 // Legacy applications pass SubscriptionManager.DEFAULT_SUB_ID,
                 // force all illegal subId to SubscriptionManager.DEFAULT_SUB_ID
                 if (!SubscriptionManager.isValidSubscriptionId(subId)) {
@@ -558,7 +565,7 @@
                     if ((events & PhoneStateListener.LISTEN_CALL_STATE) != 0) {
                         try {
                             r.callback.onCallStateChanged(mCallState[phoneId],
-                                     mCallIncomingNumber[phoneId]);
+                                     getCallIncomingNumber(r, phoneId));
                         } catch (RemoteException ex) {
                             remove(r.binder);
                         }
@@ -638,6 +645,22 @@
         }
     }
 
+    private boolean canReadPhoneState(String callingPackage) {
+        boolean canReadPhoneState = mContext.checkCallingOrSelfPermission(
+                android.Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED;
+        if (canReadPhoneState &&
+                mAppOps.noteOp(AppOpsManager.OP_READ_PHONE_STATE, Binder.getCallingUid(),
+                        callingPackage) != AppOpsManager.MODE_ALLOWED) {
+            return false;
+        }
+        return canReadPhoneState;
+    }
+
+    private String getCallIncomingNumber(Record record, int phoneId) {
+        // Hide the number if record's process has no READ_PHONE_STATE permission
+        return record.canReadPhoneState ? mCallIncomingNumber[phoneId] : "";
+    }
+
     private void remove(IBinder binder) {
         synchronized (mRecords) {
             final int recordCount = mRecords.size();
@@ -669,7 +692,8 @@
                 if (r.matchPhoneStateListenerEvent(PhoneStateListener.LISTEN_CALL_STATE) &&
                         (r.subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID)) {
                     try {
-                        r.callback.onCallStateChanged(state, incomingNumber);
+                        String incomingNumberOrEmpty = r.canReadPhoneState ? incomingNumber : "";
+                        r.callback.onCallStateChanged(state, incomingNumberOrEmpty);
                     } catch (RemoteException ex) {
                         mRemoveList.add(r.binder);
                     }
@@ -699,7 +723,8 @@
                             (r.subId == subId) &&
                             (r.subId != SubscriptionManager.DEFAULT_SUBSCRIPTION_ID)) {
                         try {
-                            r.callback.onCallStateChanged(state, incomingNumber);
+                            String incomingNumberOrEmpty = getCallIncomingNumber(r, phoneId);
+                            r.callback.onCallStateChanged(state, incomingNumberOrEmpty);
                         } catch (RemoteException ex) {
                             mRemoveList.add(r.binder);
                         }
@@ -1538,7 +1563,7 @@
 
         }
 
-        if ((events & PHONE_STATE_PERMISSION_MASK) != 0) {
+        if ((events & ENFORCE_PHONE_STATE_PERMISSION_MASK) != 0) {
             mContext.enforceCallingOrSelfPermission(
                     android.Manifest.permission.READ_PHONE_STATE, null);
         }
@@ -1572,10 +1597,10 @@
         boolean valid = false;
         try {
             foregroundUser = ActivityManager.getCurrentUser();
-            valid = r.callerUid ==  foregroundUser && r.matchPhoneStateListenerEvent(events);
+            valid = r.callerUserId ==  foregroundUser && r.matchPhoneStateListenerEvent(events);
             if (DBG | DBG_LOC) {
                 log("validateEventsAndUserLocked: valid=" + valid
-                        + " r.callerUid=" + r.callerUid + " foregroundUser=" + foregroundUser
+                        + " r.callerUserId=" + r.callerUserId + " foregroundUser=" + foregroundUser
                         + " r.events=" + r.events + " events=" + events);
             }
         } finally {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 1046b29..bedc729 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -468,12 +468,6 @@
      */
     String mDeviceOwnerName;
 
-    /**
-     * Preferred activities to start on boot/user switch, as set by DevicePolicyManager. Indexed
-     * by userId.
-     */
-    SparseArray<ComponentName> mPreferredSetupActivities = new SparseArray<>();
-
     public class PendingAssistExtras extends Binder implements Runnable {
         public final ActivityRecord activity;
         public final Bundle extras;
@@ -3435,20 +3429,13 @@
                 // Factory test.
                 ai = AppGlobals.getPackageManager().getActivityInfo(comp, flags, userId);
             } else {
-                ComponentName preferredComponent = mPreferredSetupActivities.get(userId);
-                if (preferredComponent != null) {
-                    ai = AppGlobals.getPackageManager().getActivityInfo(
-                            preferredComponent, flags, userId);
-                }
-                if (ai == null) {
-                    ResolveInfo info = AppGlobals.getPackageManager().resolveIntent(
-                            intent,
-                            intent.resolveTypeIfNeeded(mContext.getContentResolver()),
-                                flags, userId);
+                ResolveInfo info = AppGlobals.getPackageManager().resolveIntent(
+                        intent,
+                        intent.resolveTypeIfNeeded(mContext.getContentResolver()),
+                        flags, userId);
 
-                    if (info != null) {
-                        ai = info.activityInfo;
-                    }
+                if (info != null) {
+                    ai = info.activityInfo;
                 }
             }
         } catch (RemoteException e) {
@@ -8919,22 +8906,6 @@
     }
 
     @Override
-    public void updatePreferredSetupActivity(ComponentName preferredActivity, int userId) {
-        final int callingUid = Binder.getCallingUid();
-        if (callingUid != 0 && callingUid != Process.SYSTEM_UID) {
-            throw new SecurityException(
-                    "updatePreferredSetupActivity called from non-system process");
-        }
-        synchronized (this) {
-            if (preferredActivity == null) {
-                mPreferredSetupActivities.delete(userId);
-            } else {
-                mPreferredSetupActivities.put(userId, preferredActivity);
-            }
-        }
-    }
-
-    @Override
     public void updateDeviceOwner(String packageName) {
         final int callingUid = Binder.getCallingUid();
         if (callingUid != 0 && callingUid != Process.SYSTEM_UID) {
diff --git a/services/core/java/com/android/server/power/ShutdownThread.java b/services/core/java/com/android/server/power/ShutdownThread.java
index 4b62c40..dd8648d 100644
--- a/services/core/java/com/android/server/power/ShutdownThread.java
+++ b/services/core/java/com/android/server/power/ShutdownThread.java
@@ -67,6 +67,12 @@
     private static final int MAX_SHUTDOWN_WAIT_TIME = 20*1000;
     private static final int MAX_RADIO_WAIT_TIME = 12*1000;
     private static final int MAX_UNCRYPT_WAIT_TIME = 15*60*1000;
+    // constants for progress bar. the values are roughly estimated based on timeout.
+    private static final int BROADCAST_STOP_PERCENT = 2;
+    private static final int ACTIVITY_MANAGER_STOP_PERCENT = 4;
+    private static final int PACKAGE_MANAGER_STOP_PERCENT = 6;
+    private static final int RADIO_STOP_PERCENT = 18;
+    private static final int MOUNT_SERVICE_STOP_PERCENT = 20;
 
     // length of vibration before shutting down
     private static final int SHUTDOWN_VIBRATE_MS = 500;
@@ -75,11 +81,13 @@
     private static Object sIsStartedGuard = new Object();
     private static boolean sIsStarted = false;
 
-    // uncrypt status file
+    // uncrypt status files
     private static final String UNCRYPT_STATUS_FILE = "/cache/recovery/uncrypt_status";
+    private static final String UNCRYPT_PACKAGE_FILE = "/cache/recovery/uncrypt_file";
 
     private static boolean mReboot;
     private static boolean mRebootSafeMode;
+    private static boolean mRebootUpdate;
     private static String mRebootReason;
 
     // Provides shutdown assurance in case the system_server is killed
@@ -203,6 +211,7 @@
     public static void reboot(final Context context, String reason, boolean confirm) {
         mReboot = true;
         mRebootSafeMode = false;
+        mRebootUpdate = false;
         mRebootReason = reason;
         shutdownInner(context, confirm);
     }
@@ -222,6 +231,7 @@
 
         mReboot = true;
         mRebootSafeMode = true;
+        mRebootUpdate = false;
         mRebootReason = null;
         shutdownInner(context, confirm);
     }
@@ -235,16 +245,44 @@
             sIsStarted = true;
         }
 
-        // throw up an indeterminate system dialog to indicate radio is
-        // shutting down.
+        // Throw up a system dialog to indicate the device is rebooting / shutting down.
         ProgressDialog pd = new ProgressDialog(context);
+
+        // Path 1: Reboot to recovery and install the update
+        //   Condition: mRebootReason == REBOOT_RECOVERY and mRebootUpdate == True
+        //   (mRebootUpdate is set by checking if /cache/recovery/uncrypt_file exists.)
+        //   UI: progress bar
+        //
+        // Path 2: Reboot to recovery for factory reset
+        //   Condition: mRebootReason == REBOOT_RECOVERY
+        //   UI: spinning circle only (no progress bar)
+        //
+        // Path 3: Regular reboot / shutdown
+        //   Condition: Otherwise
+        //   UI: spinning circle only (no progress bar)
         if (PowerManager.REBOOT_RECOVERY.equals(mRebootReason)) {
-            pd.setTitle(context.getText(com.android.internal.R.string.reboot_to_recovery_title));
+            mRebootUpdate = new File(UNCRYPT_PACKAGE_FILE).exists();
+            if (mRebootUpdate) {
+                pd.setTitle(context.getText(com.android.internal.R.string.reboot_to_update_title));
+                pd.setMessage(context.getText(
+                        com.android.internal.R.string.reboot_to_update_prepare));
+                pd.setMax(100);
+                pd.setProgressNumberFormat(null);
+                pd.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
+                pd.setProgress(0);
+                pd.setIndeterminate(false);
+            } else {
+                // Factory reset path. Set the dialog message accordingly.
+                pd.setTitle(context.getText(com.android.internal.R.string.reboot_to_reset_title));
+                pd.setMessage(context.getText(
+                        com.android.internal.R.string.reboot_to_reset_message));
+                pd.setIndeterminate(true);
+            }
         } else {
             pd.setTitle(context.getText(com.android.internal.R.string.power_off));
+            pd.setMessage(context.getText(com.android.internal.R.string.shutdown_progress));
+            pd.setIndeterminate(true);
         }
-        pd.setMessage(context.getText(com.android.internal.R.string.shutdown_progress));
-        pd.setIndeterminate(true);
         pd.setCancelable(false);
         pd.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
 
@@ -339,13 +377,20 @@
                 if (delay <= 0) {
                     Log.w(TAG, "Shutdown broadcast timed out");
                     break;
+                } else if (mRebootUpdate) {
+                    int status = (int)((MAX_BROADCAST_TIME - delay) * 1.0 *
+                            BROADCAST_STOP_PERCENT / MAX_BROADCAST_TIME);
+                    sInstance.setRebootProgress(status, null);
                 }
                 try {
-                    mActionDoneSync.wait(delay);
+                    mActionDoneSync.wait(Math.min(delay, PHONE_STATE_POLL_SLEEP_MSEC));
                 } catch (InterruptedException e) {
                 }
             }
         }
+        if (mRebootUpdate) {
+            sInstance.setRebootProgress(BROADCAST_STOP_PERCENT, null);
+        }
 
         Log.i(TAG, "Shutting down activity manager...");
 
@@ -357,6 +402,9 @@
             } catch (RemoteException e) {
             }
         }
+        if (mRebootUpdate) {
+            sInstance.setRebootProgress(ACTIVITY_MANAGER_STOP_PERCENT, null);
+        }
 
         Log.i(TAG, "Shutting down package manager...");
 
@@ -365,9 +413,15 @@
         if (pm != null) {
             pm.shutdown();
         }
+        if (mRebootUpdate) {
+            sInstance.setRebootProgress(PACKAGE_MANAGER_STOP_PERCENT, null);
+        }
 
         // Shutdown radios.
         shutdownRadios(MAX_RADIO_WAIT_TIME);
+        if (mRebootUpdate) {
+            sInstance.setRebootProgress(RADIO_STOP_PERCENT, null);
+        }
 
         // Shutdown MountService to ensure media is in a safe state
         IMountShutdownObserver observer = new IMountShutdownObserver.Stub() {
@@ -399,64 +453,44 @@
                 if (delay <= 0) {
                     Log.w(TAG, "Shutdown wait timed out");
                     break;
+                } else if (mRebootUpdate) {
+                    int status = (int)((MAX_SHUTDOWN_WAIT_TIME - delay) * 1.0 *
+                            (MOUNT_SERVICE_STOP_PERCENT - RADIO_STOP_PERCENT) /
+                            MAX_SHUTDOWN_WAIT_TIME);
+                    status += RADIO_STOP_PERCENT;
+                    sInstance.setRebootProgress(status, null);
                 }
                 try {
-                    mActionDoneSync.wait(delay);
+                    mActionDoneSync.wait(Math.min(delay, PHONE_STATE_POLL_SLEEP_MSEC));
                 } catch (InterruptedException e) {
                 }
             }
         }
+        if (mRebootUpdate) {
+            sInstance.setRebootProgress(MOUNT_SERVICE_STOP_PERCENT, null);
 
-        // If it's to reboot into recovery, invoke uncrypt via init service.
-        if (PowerManager.REBOOT_RECOVERY.equals(mRebootReason)) {
+            // If it's to reboot to install update, invoke uncrypt via init service.
             uncrypt();
         }
 
         rebootOrShutdown(mContext, mReboot, mRebootReason);
     }
 
-    private void prepareUncryptProgress() {
-        // Reset the dialog message to show the decrypt process.
-        mHandler.post(new Runnable() {
-            @Override
-            public void run() {
-                if (mProgressDialog != null) {
-                    mProgressDialog.dismiss();
-                }
-                // It doesn't work to change the style of the existing
-                // one. Have to create a new one.
-                ProgressDialog pd = new ProgressDialog(mContext);
-
-                pd.setTitle(mContext.getText(
-                        com.android.internal.R.string.reboot_to_recovery_title));
-                pd.setMessage(mContext.getText(
-                        com.android.internal.R.string.reboot_to_recovery_progress));
-                pd.setIndeterminate(false);
-                pd.setMax(100);
-                pd.setCancelable(false);
-                pd.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
-                pd.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
-                pd.setProgressNumberFormat(null);
-                pd.setProgress(0);
-
-                mProgressDialog = pd;
-                mProgressDialog.show();
-            }
-        });
-    }
-
-    private void setUncryptProgress(final int progress) {
+    private void setRebootProgress(final int progress, final CharSequence message) {
         mHandler.post(new Runnable() {
             @Override
             public void run() {
                 if (mProgressDialog != null) {
                     mProgressDialog.setProgress(progress);
+                    if (message != null) {
+                        mProgressDialog.setMessage(message);
+                    }
                 }
             }
         });
     }
 
-    private void shutdownRadios(int timeout) {
+    private void shutdownRadios(final int timeout) {
         // If a radio is wedged, disabling it may hang so we do this work in another thread,
         // just in case.
         final long endTime = SystemClock.elapsedRealtime() + timeout;
@@ -511,7 +545,15 @@
 
                 Log.i(TAG, "Waiting for NFC, Bluetooth and Radio...");
 
-                while (SystemClock.elapsedRealtime() < endTime) {
+                long delay = endTime - SystemClock.elapsedRealtime();
+                while (delay > 0) {
+                    if (mRebootUpdate) {
+                        int status = (int)((timeout - delay) * 1.0 *
+                                (RADIO_STOP_PERCENT - PACKAGE_MANAGER_STOP_PERCENT) / timeout);
+                        status += PACKAGE_MANAGER_STOP_PERCENT;
+                        sInstance.setRebootProgress(status, null);
+                    }
+
                     if (!bluetoothOff) {
                         try {
                             bluetoothOff = !bluetooth.isEnabled();
@@ -552,6 +594,8 @@
                         break;
                     }
                     SystemClock.sleep(PHONE_STATE_POLL_SLEEP_MSEC);
+
+                    delay = endTime - SystemClock.elapsedRealtime();
                 }
             }
         };
@@ -604,9 +648,6 @@
     private void uncrypt() {
         Log.i(TAG, "Calling uncrypt and monitoring the progress...");
 
-        // Update the ProcessDialog message and style.
-        sInstance.prepareUncryptProgress();
-
         final boolean[] done = new boolean[1];
         done[0] = false;
         Thread t = new Thread() {
@@ -627,25 +668,32 @@
                 try (BufferedReader reader = new BufferedReader(
                         new FileReader(UNCRYPT_STATUS_FILE))) {
 
-                    int last_status = Integer.MIN_VALUE;
+                    int lastStatus = Integer.MIN_VALUE;
                     while (true) {
                         String str = reader.readLine();
                         try {
                             int status = Integer.parseInt(str);
 
                             // Avoid flooding the log with the same message.
-                            if (status == last_status && last_status != Integer.MIN_VALUE) {
+                            if (status == lastStatus && lastStatus != Integer.MIN_VALUE) {
                                 continue;
                             }
-                            last_status = status;
+                            lastStatus = status;
 
                             if (status >= 0 && status < 100) {
                                 // Update status
                                 Log.d(TAG, "uncrypt read status: " + status);
-                                sInstance.setUncryptProgress(status);
+                                // Scale down to [MOUNT_SERVICE_STOP_PERCENT, 100).
+                                status = (int)(status * (100.0 - MOUNT_SERVICE_STOP_PERCENT) / 100);
+                                status += MOUNT_SERVICE_STOP_PERCENT;
+                                CharSequence msg = mContext.getText(
+                                        com.android.internal.R.string.reboot_to_update_package);
+                                sInstance.setRebootProgress(status, msg);
                             } else if (status == 100) {
                                 Log.d(TAG, "uncrypt successfully finished.");
-                                sInstance.setUncryptProgress(status);
+                                CharSequence msg = mContext.getText(
+                                        com.android.internal.R.string.reboot_to_update_reboot);
+                                sInstance.setRebootProgress(status, msg);
                                 break;
                             } else {
                                 // Error in /system/bin/uncrypt. Or it's rebooting to recovery
diff --git a/services/core/jni/com_android_server_UsbMidiDevice.cpp b/services/core/jni/com_android_server_UsbMidiDevice.cpp
index cb70144..06b9bc3 100644
--- a/services/core/jni/com_android_server_UsbMidiDevice.cpp
+++ b/services/core/jni/com_android_server_UsbMidiDevice.cpp
@@ -36,6 +36,7 @@
 {
 
 static jclass sFileDescriptorClass;
+static jfieldID sPipeFDField;
 
 static jint
 android_server_UsbMidiDevice_get_subdevice_count(JNIEnv *env, jobject /* thiz */,
@@ -66,14 +67,15 @@
 }
 
 static jobjectArray
-android_server_UsbMidiDevice_open(JNIEnv *env, jobject /* thiz */, jint card, jint device,
+android_server_UsbMidiDevice_open(JNIEnv *env, jobject thiz, jint card, jint device,
         jint subdevice_count)
 {
     char    path[100];
 
     snprintf(path, sizeof(path), "/dev/snd/midiC%dD%d", card, device);
 
-    jobjectArray fds = env->NewObjectArray(subdevice_count, sFileDescriptorClass, NULL);
+    // allocate one extra file descriptor for close pipe
+    jobjectArray fds = env->NewObjectArray(subdevice_count + 1, sFileDescriptorClass, NULL);
     if (!fds) {
         return NULL;
     }
@@ -91,12 +93,27 @@
         env->DeleteLocalRef(fileDescriptor);
     }
 
+    // create a pipe to use for unblocking our input thread
+    int pipeFD[2];
+    pipe(pipeFD);
+    jobject fileDescriptor = jniCreateFileDescriptor(env, pipeFD[0]);
+    env->SetObjectArrayElement(fds, subdevice_count, fileDescriptor);
+    env->DeleteLocalRef(fileDescriptor);
+    // store our end of the pipe in mPipeFD
+    env->SetIntField(thiz, sPipeFDField, pipeFD[1]);
+
     return fds;
 }
 
 static void
-android_server_UsbMidiDevice_close(JNIEnv *env, jobject /* thiz */, jobjectArray fds)
+android_server_UsbMidiDevice_close(JNIEnv *env, jobject thiz, jobjectArray fds)
 {
+    // write to mPipeFD to unblock input thread
+    jint pipeFD = env->GetIntField(thiz, sPipeFDField);
+    write(pipeFD, &pipeFD, sizeof(pipeFD));
+    close(pipeFD);
+    env->SetIntField(thiz, sPipeFDField, -1);
+
     int count = env->GetArrayLength(fds);
     for (int i = 0; i < count; i++) {
         jobject fd = env->GetObjectArrayElement(fds, i);
@@ -117,13 +134,18 @@
         ALOGE("Can't find java/io/FileDescriptor");
         return -1;
     }
-    sFileDescriptorClass = (jclass)env->NewGlobalRef(clazz);;
+    sFileDescriptorClass = (jclass)env->NewGlobalRef(clazz);
 
     clazz = env->FindClass("com/android/server/usb/UsbMidiDevice");
     if (clazz == NULL) {
         ALOGE("Can't find com/android/server/usb/UsbMidiDevice");
         return -1;
     }
+    sPipeFDField = env->GetFieldID(clazz, "mPipeFD", "I");
+    if (sPipeFDField == NULL) {
+        ALOGE("Can't find UsbMidiDevice.mPipeFD");
+        return -1;
+    }
 
     return jniRegisterNativeMethods(env, "com/android/server/usb/UsbMidiDevice",
             method_table, NELEM(method_table));
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 825ef1a..feb0285 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -183,7 +183,6 @@
 
     private static final String ATTR_PERMISSION_PROVIDER = "permission-provider";
     private static final String ATTR_SETUP_COMPLETE = "setup-complete";
-    private static final String ATTR_PREFERRED_SETUP_ACTIVITY = "setup-activity";
     private static final String ATTR_PERMISSION_POLICY = "permission-policy";
 
     private static final String ATTR_DELEGATED_CERT_INSTALLER = "delegated-cert-installer";
@@ -335,8 +334,6 @@
 
         boolean doNotAskCredentialsOnBoot = false;
 
-        ComponentName mPreferredSetupActivity;
-
         public DevicePolicyData(int userHandle) {
             mUserHandle = userHandle;
         }
@@ -1436,12 +1433,6 @@
                 out.attribute(null, ATTR_DELEGATED_CERT_INSTALLER,
                         policy.mDelegatedCertInstallerPackage);
             }
-            if (policy.mPreferredSetupActivity != null) {
-                out.attribute(null, ATTR_PREFERRED_SETUP_ACTIVITY,
-                        policy.mPreferredSetupActivity.flattenToString());
-            } else {
-                out.attribute(null, ATTR_PREFERRED_SETUP_ACTIVITY, "");
-            }
 
             final int N = policy.mAdminList.size();
             for (int i=0; i<N; i++) {
@@ -1566,12 +1557,6 @@
             }
             policy.mDelegatedCertInstallerPackage = parser.getAttributeValue(null,
                     ATTR_DELEGATED_CERT_INSTALLER);
-            String preferredSetupActivity =
-                    parser.getAttributeValue(null, ATTR_PREFERRED_SETUP_ACTIVITY);
-            if (preferredSetupActivity != null) {
-                policy.mPreferredSetupActivity =
-                        ComponentName.unflattenFromString(preferredSetupActivity);
-            }
 
             type = parser.next();
             int outerDepth = parser.getDepth();
@@ -1695,7 +1680,6 @@
         if (policy.mStatusBarDisabled) {
             setStatusBarDisabledInternal(policy.mStatusBarDisabled, userHandle);
         }
-        updatePreferredSetupActivityLocked(userHandle);
     }
 
     private void updateLockTaskPackagesLocked(List<String> packages, int userId) {
@@ -4734,43 +4718,6 @@
     }
 
     @Override
-    public void setPreferredSetupActivity(ComponentName who, ComponentName activity) {
-        if (!mHasFeature) {
-            return;
-        }
-        Preconditions.checkNotNull(who, "ComponentName is null");
-        synchronized (this) {
-            ActiveAdmin activeAdmin =
-                    getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
-            if (!isDeviceInitializer(activeAdmin.info.getPackageName())) {
-                throw new SecurityException(
-                        "This method can only be called by device initializers");
-            }
-            int userHandle = UserHandle.getCallingUserId();
-            DevicePolicyData userData = getUserData(userHandle);
-            userData.mPreferredSetupActivity = activity;
-            saveSettingsLocked(userHandle);
-            updatePreferredSetupActivityLocked(userHandle);
-        }
-    }
-
-    private void updatePreferredSetupActivityLocked(int userHandle) {
-        if (!mHasFeature) {
-            return;
-        }
-        IActivityManager am = ActivityManagerNative.getDefault();
-        long ident = Binder.clearCallingIdentity();
-        try {
-            am.updatePreferredSetupActivity(
-                    getUserData(userHandle).mPreferredSetupActivity, userHandle);
-        } catch (RemoteException e) {
-            // Not gonna happen.
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
-    }
-
-    @Override
     public void setApplicationRestrictions(ComponentName who, String packageName, Bundle settings) {
         Preconditions.checkNotNull(who, "ComponentName is null");
         final UserHandle userHandle = new UserHandle(UserHandle.getCallingUserId());
@@ -6138,9 +6085,6 @@
                 if (!policy.mUserSetupComplete) {
                     policy.mUserSetupComplete = true;
                     synchronized (this) {
-                        // Clear the preferred setup activity.
-                        policy.mPreferredSetupActivity = null;
-                        updatePreferredSetupActivityLocked(userHandle);
                         // The DeviceInitializer was whitelisted but now should be removed.
                         removeDeviceInitializerFromLockTaskPackages(userHandle);
                         saveSettingsLocked(userHandle);
diff --git a/services/usb/java/com/android/server/usb/UsbMidiDevice.java b/services/usb/java/com/android/server/usb/UsbMidiDevice.java
index 671cf01..97bf505 100644
--- a/services/usb/java/com/android/server/usb/UsbMidiDevice.java
+++ b/services/usb/java/com/android/server/usb/UsbMidiDevice.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 import android.media.midi.MidiDeviceInfo;
 import android.media.midi.MidiDeviceServer;
+import android.media.midi.MidiDeviceStatus;
 import android.media.midi.MidiManager;
 import android.media.midi.MidiReceiver;
 import android.media.midi.MidiSender;
@@ -43,38 +44,100 @@
 public final class UsbMidiDevice implements Closeable {
     private static final String TAG = "UsbMidiDevice";
 
+    private final int mAlsaCard;
+    private final int mAlsaDevice;
+    private final int mSubdeviceCount;
+    private final InputReceiverProxy[] mInputPortReceivers;
+
     private MidiDeviceServer mServer;
 
     // event schedulers for each output port
-    private final MidiEventScheduler[] mEventSchedulers;
+    private MidiEventScheduler[] mEventSchedulers;
 
     private static final int BUFFER_SIZE = 512;
 
-    private final FileDescriptor[] mFileDescriptors;
+    private FileDescriptor[] mFileDescriptors;
 
     // for polling multiple FileDescriptors for MIDI events
-    private final StructPollfd[] mPollFDs;
+    private StructPollfd[] mPollFDs;
     // streams for reading from ALSA driver
-    private final FileInputStream[] mInputStreams;
+    private FileInputStream[] mInputStreams;
     // streams for writing to ALSA driver
-    private final FileOutputStream[] mOutputStreams;
+    private FileOutputStream[] mOutputStreams;
 
+    private final Object mLock = new Object();
+    private boolean mIsOpen;
+
+    // pipe file descriptor for signalling input thread to exit
+    // only accessed from JNI code
+    private int mPipeFD = -1;
+
+    private final MidiDeviceServer.Callback mCallback = new MidiDeviceServer.Callback() {
+
+        @Override
+        public void onDeviceStatusChanged(MidiDeviceServer server, MidiDeviceStatus status) {
+            MidiDeviceInfo deviceInfo = status.getDeviceInfo();
+            int inputPorts = deviceInfo.getInputPortCount();
+            int outputPorts = deviceInfo.getOutputPortCount();
+            boolean hasOpenPorts = false;
+
+            for (int i = 0; i < inputPorts; i++) {
+                if (status.isInputPortOpen(i)) {
+                    hasOpenPorts = true;
+                    break;
+                }
+            }
+
+            if (!hasOpenPorts) {
+                for (int i = 0; i < outputPorts; i++) {
+                    if (status.getOutputPortOpenCount(i) > 0) {
+                        hasOpenPorts = true;
+                        break;
+                    }
+                }
+            }
+
+            synchronized (mLock) {
+                if (hasOpenPorts && !mIsOpen) {
+                    openLocked();
+                } else if (!hasOpenPorts && mIsOpen) {
+                    closeLocked();
+                }
+            }
+        }
+
+        @Override
+        public void onClose() {
+        }
+    };
+
+    // This class acts as a proxy for our MidiEventScheduler receivers, which do not exist
+    // until the device has active clients
+    private final class InputReceiverProxy extends MidiReceiver {
+        private MidiReceiver mReceiver;
+
+        @Override
+        public void onSend(byte[] msg, int offset, int count, long timestamp) throws IOException {
+            MidiReceiver receiver = mReceiver;
+            if (receiver != null) {
+                receiver.send(msg, offset, count, timestamp);
+            }
+        }
+
+        public void setReceiver(MidiReceiver receiver) {
+            mReceiver = receiver;
+        }
+    }
+    
     public static UsbMidiDevice create(Context context, Bundle properties, int card, int device) {
         // FIXME - support devices with different number of input and output ports
-        int subDevices = nativeGetSubdeviceCount(card, device);
-        if (subDevices <= 0) {
+        int subDeviceCount = nativeGetSubdeviceCount(card, device);
+        if (subDeviceCount <= 0) {
             Log.e(TAG, "nativeGetSubdeviceCount failed");
             return null;
         }
 
-        // FIXME - support devices with different number of input and output ports
-        FileDescriptor[] fileDescriptors = nativeOpen(card, device, subDevices);
-        if (fileDescriptors == null) {
-            Log.e(TAG, "nativeOpen failed");
-            return null;
-        }
-
-        UsbMidiDevice midiDevice = new UsbMidiDevice(fileDescriptors);
+        UsbMidiDevice midiDevice = new UsbMidiDevice(card, device, subDeviceCount);
         if (!midiDevice.register(context, properties)) {
             IoUtils.closeQuietly(midiDevice);
             Log.e(TAG, "createDeviceServer failed");
@@ -83,10 +146,32 @@
         return midiDevice;
     }
 
-    private UsbMidiDevice(FileDescriptor[] fileDescriptors) {
+    private UsbMidiDevice(int card, int device, int subdeviceCount) {
+        mAlsaCard = card;
+        mAlsaDevice = device;
+        mSubdeviceCount = subdeviceCount;
+
+        // FIXME - support devices with different number of input and output ports
+        int inputCount = subdeviceCount;
+        mInputPortReceivers = new InputReceiverProxy[inputCount];
+        for (int port = 0; port < inputCount; port++) {
+            mInputPortReceivers[port] = new InputReceiverProxy();
+        }
+    }
+
+    private boolean openLocked() {
+        // FIXME - support devices with different number of input and output ports
+        FileDescriptor[] fileDescriptors = nativeOpen(mAlsaCard, mAlsaDevice, mSubdeviceCount);
+        if (fileDescriptors == null) {
+            Log.e(TAG, "nativeOpen failed");
+            return false;
+        }
+
         mFileDescriptors = fileDescriptors;
         int inputCount = fileDescriptors.length;
-        int outputCount = fileDescriptors.length;
+        // last file descriptor returned from nativeOpen() is only used for unblocking Os.poll()
+        // in our input thread
+        int outputCount = fileDescriptors.length - 1;
 
         mPollFDs = new StructPollfd[inputCount];
         mInputStreams = new FileInputStream[inputCount];
@@ -103,29 +188,12 @@
         mEventSchedulers = new MidiEventScheduler[outputCount];
         for (int i = 0; i < outputCount; i++) {
             mOutputStreams[i] = new FileOutputStream(fileDescriptors[i]);
-            mEventSchedulers[i] = new MidiEventScheduler();
-        }
-    }
 
-    private boolean register(Context context, Bundle properties) {
-        MidiManager midiManager = (MidiManager)context.getSystemService(Context.MIDI_SERVICE);
-        if (midiManager == null) {
-            Log.e(TAG, "No MidiManager in UsbMidiDevice.create()");
-            return false;
+            MidiEventScheduler scheduler = new MidiEventScheduler();
+            mEventSchedulers[i] = scheduler;
+            mInputPortReceivers[i].setReceiver(scheduler.getReceiver());
         }
 
-        int inputCount = mInputStreams.length;
-        int outputCount = mOutputStreams.length;
-        MidiReceiver[] inputPortReceivers = new MidiReceiver[inputCount];
-        for (int port = 0; port < inputCount; port++) {
-            inputPortReceivers[port] = mEventSchedulers[port].getReceiver();
-        }
-
-        mServer = midiManager.createDeviceServer(inputPortReceivers, outputCount,
-                null, null, properties, MidiDeviceInfo.TYPE_USB, null);
-        if (mServer == null) {
-            return false;
-        }
         final MidiReceiver[] outputReceivers = mServer.getOutputPortReceivers();
 
         // Create input thread which will read from all input ports
@@ -134,24 +202,32 @@
             public void run() {
                 byte[] buffer = new byte[BUFFER_SIZE];
                 try {
-                    boolean done = false;
-                    while (!done) {
-                        // look for a readable FileDescriptor
-                        for (int index = 0; index < mPollFDs.length; index++) {
-                            StructPollfd pfd = mPollFDs[index];
-                            if ((pfd.revents & OsConstants.POLLIN) != 0) {
-                                // clear readable flag
-                                pfd.revents = 0;
+                    while (true) {
+                        synchronized (mLock) {
+                            if (!mIsOpen) break;
 
-                                int count = mInputStreams[index].read(buffer);
-                                outputReceivers[index].send(buffer, 0, count);
-                            } else if ((pfd.revents & (OsConstants.POLLERR
-                                                        | OsConstants.POLLHUP)) != 0) {
-                                done = true;
+                            // look for a readable FileDescriptor
+                            for (int index = 0; index < mPollFDs.length; index++) {
+                                StructPollfd pfd = mPollFDs[index];
+                                if ((pfd.revents & (OsConstants.POLLERR
+                                                            | OsConstants.POLLHUP)) != 0) {
+                                    break;
+                                } else if ((pfd.revents & OsConstants.POLLIN) != 0) {
+                                    // clear readable flag
+                                    pfd.revents = 0;
+                                    
+                                    if (index == mInputStreams.length - 1) {
+                                        // last file descriptor is used only for unblocking Os.poll()
+                                        break;
+                                    }
+
+                                    int count = mInputStreams[index].read(buffer);
+                                    outputReceivers[index].send(buffer, 0, count);
+                                }
                             }
                         }
 
-                        // wait until we have a readable port
+                        // wait until we have a readable port or we are signalled to close
                         Os.poll(mPollFDs, -1 /* infinite timeout */);
                      }
                 } catch (IOException e) {
@@ -195,29 +271,64 @@
             }.start();
         }
 
+        mIsOpen = true;
+        return true;
+    }
+
+    private boolean register(Context context, Bundle properties) {
+        MidiManager midiManager = (MidiManager)context.getSystemService(Context.MIDI_SERVICE);
+        if (midiManager == null) {
+            Log.e(TAG, "No MidiManager in UsbMidiDevice.create()");
+            return false;
+        }
+
+        mServer = midiManager.createDeviceServer(mInputPortReceivers, mSubdeviceCount,
+                null, null, properties, MidiDeviceInfo.TYPE_USB, mCallback);
+        if (mServer == null) {
+            return false;
+        }
+
         return true;
     }
 
     @Override
     public void close() throws IOException {
-        for (int i = 0; i < mEventSchedulers.length; i++) {
-            mEventSchedulers[i].close();
+        synchronized (mLock) {
+            if (mIsOpen) {
+                closeLocked();
+            }
         }
 
         if (mServer != null) {
-            mServer.close();
+            IoUtils.closeQuietly(mServer);
         }
+    }
+
+    private void closeLocked() {
+        for (int i = 0; i < mEventSchedulers.length; i++) {
+            mInputPortReceivers[i].setReceiver(null);
+            mEventSchedulers[i].close();
+        }
+        mEventSchedulers = null;
 
         for (int i = 0; i < mInputStreams.length; i++) {
-            mInputStreams[i].close();
+            IoUtils.closeQuietly(mInputStreams[i]);
         }
+        mInputStreams = null;
+
         for (int i = 0; i < mOutputStreams.length; i++) {
-            mOutputStreams[i].close();
+            IoUtils.closeQuietly(mOutputStreams[i]);
         }
+        mOutputStreams = null;
+
+        // nativeClose will close the file descriptors and signal the input thread to exit
         nativeClose(mFileDescriptors);
+        mFileDescriptors = null;
+
+        mIsOpen = false;
     }
 
     private static native int nativeGetSubdeviceCount(int card, int device);
-    private static native FileDescriptor[] nativeOpen(int card, int device, int subdeviceCount);
-    private static native void nativeClose(FileDescriptor[] fileDescriptors);
+    private native FileDescriptor[] nativeOpen(int card, int device, int subdeviceCount);
+    private native void nativeClose(FileDescriptor[] fileDescriptors);
 }
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index bb210f1..0042414 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -838,7 +838,8 @@
          * <p>
          * This could be in response to a preview request via
          * {@link #onRequestConnectionDataUsage()}, or as a periodic update by the
-         * {@link VideoProvider}.
+         * {@link VideoProvider}.  Where periodic updates of data usage are provided, they should be
+         * provided at most for every 1 MB of data transferred and no more than once every 10 sec.
          * <p>
          * Received by the {@link InCallService} via
          * {@link InCallService.VideoCall.Callback#onCallDataUsageChanged(long)}.
diff --git a/telecomm/java/android/telecom/InCallService.java b/telecomm/java/android/telecom/InCallService.java
index f7f4425..fb985ce 100644
--- a/telecomm/java/android/telecom/InCallService.java
+++ b/telecomm/java/android/telecom/InCallService.java
@@ -413,6 +413,8 @@
 
         /**
          * Clears the video call callback set via {@link #registerCallback}.
+         *
+         * @param callback The video call callback to clear.
          */
         public abstract void unregisterCallback(VideoCall.Callback callback);
 
@@ -524,7 +526,8 @@
 
         /**
          * The {@link InCallService} extends this class to provide a means of receiving callbacks
-         * from the {@link Connection.VideoProvider}.<p>
+         * from the {@link Connection.VideoProvider}.
+         * <p>
          * When the {@link InCallService} receives the
          * {@link Call.Callback#onVideoCallChanged(Call, VideoCall)} callback, it should create an
          * instance its {@link VideoCall.Callback} implementation and set it on the
@@ -533,7 +536,7 @@
         public static abstract class Callback {
             /**
              * Called when the {@link Connection.VideoProvider} receives a session modification
-             * request is received from the peer device.
+             * request from the peer device.
              * <p>
              * The {@link InCallService} may potentially prompt the user to confirm whether they
              * wish to accept the request, or decide to automatically accept the request.  In either
diff --git a/telecomm/java/android/telecom/RemoteConnection.java b/telecomm/java/android/telecom/RemoteConnection.java
index d62c08e..8f7b82f 100644
--- a/telecomm/java/android/telecom/RemoteConnection.java
+++ b/telecomm/java/android/telecom/RemoteConnection.java
@@ -22,6 +22,7 @@
 
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
+import android.hardware.camera2.CameraManager;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Handler;
@@ -207,29 +208,111 @@
         public void onExtrasChanged(RemoteConnection connection, @Nullable Bundle extras) {}
     }
 
+    /**
+     * {@link RemoteConnection.VideoProvider} associated with a {@link RemoteConnection}.  Used to
+     * receive video related events and control the video associated with a
+     * {@link RemoteConnection}.
+     *
+     * @see Connection.VideoProvider
+     */
     public static class VideoProvider {
 
+        /**
+         * Callback class used by the {@link RemoteConnection.VideoProvider} to relay events from
+         * the {@link Connection.VideoProvider}.
+         */
         public abstract static class Callback {
+            /**
+             * Reports a session modification request received from the
+             * {@link Connection.VideoProvider} associated with a {@link RemoteConnection}.
+             *
+             * @param videoProvider The {@link RemoteConnection.VideoProvider} invoking this method.
+             * @param videoProfile The requested video call profile.
+             * @see InCallService.VideoCall.Callback#onSessionModifyRequestReceived(VideoProfile)
+             * @see Connection.VideoProvider#receiveSessionModifyRequest(VideoProfile)
+             */
             public void onSessionModifyRequestReceived(
                     VideoProvider videoProvider,
                     VideoProfile videoProfile) {}
 
+            /**
+             * Reports a session modification response received from the
+             * {@link Connection.VideoProvider} associated with a {@link RemoteConnection}.
+             *
+             * @param videoProvider The {@link RemoteConnection.VideoProvider} invoking this method.
+             * @param status Status of the session modify request.
+             * @param requestedProfile The original request which was sent to the peer device.
+             * @param responseProfile The actual profile changes made by the peer device.
+             * @see InCallService.VideoCall.Callback#onSessionModifyResponseReceived(int,
+             *      VideoProfile, VideoProfile)
+             * @see Connection.VideoProvider#receiveSessionModifyResponse(int, VideoProfile,
+             *      VideoProfile)
+             */
             public void onSessionModifyResponseReceived(
                     VideoProvider videoProvider,
                     int status,
                     VideoProfile requestedProfile,
                     VideoProfile responseProfile) {}
 
+            /**
+             * Reports a call session event received from the {@link Connection.VideoProvider}
+             * associated with a {@link RemoteConnection}.
+             *
+             * @param videoProvider The {@link RemoteConnection.VideoProvider} invoking this method.
+             * @param event The event.
+             * @see InCallService.VideoCall.Callback#onCallSessionEvent(int)
+             * @see Connection.VideoProvider#handleCallSessionEvent(int)
+             */
             public void onCallSessionEvent(VideoProvider videoProvider, int event) {}
 
-            public void onPeerDimensionsChanged(VideoProvider videoProvider, int width, int height) {}
+            /**
+             * Reports a change in the peer video dimensions received from the
+             * {@link Connection.VideoProvider} associated with a {@link RemoteConnection}.
+             *
+             * @param videoProvider The {@link RemoteConnection.VideoProvider} invoking this method.
+             * @param width  The updated peer video width.
+             * @param height The updated peer video height.
+             * @see InCallService.VideoCall.Callback#onPeerDimensionsChanged(int, int)
+             * @see Connection.VideoProvider#changePeerDimensions(int, int)
+             */
+            public void onPeerDimensionsChanged(VideoProvider videoProvider, int width,
+                    int height) {}
 
+            /**
+             * Reports a change in the data usage (in bytes) received from the
+             * {@link Connection.VideoProvider} associated with a {@link RemoteConnection}.
+             *
+             * @param videoProvider The {@link RemoteConnection.VideoProvider} invoking this method.
+             * @param dataUsage The updated data usage (in bytes).
+             * @see InCallService.VideoCall.Callback#onCallDataUsageChanged(long)
+             * @see Connection.VideoProvider#setCallDataUsage(long)
+             */
             public void onCallDataUsageChanged(VideoProvider videoProvider, long dataUsage) {}
 
+            /**
+             * Reports a change in the capabilities of the current camera, received from the
+             * {@link Connection.VideoProvider} associated with a {@link RemoteConnection}.
+             *
+             * @param videoProvider The {@link RemoteConnection.VideoProvider} invoking this method.
+             * @param cameraCapabilities The changed camera capabilities.
+             * @see InCallService.VideoCall.Callback#onCameraCapabilitiesChanged(
+             *      VideoProfile.CameraCapabilities)
+             * @see Connection.VideoProvider#changeCameraCapabilities(
+             *      VideoProfile.CameraCapabilities)
+             */
             public void onCameraCapabilitiesChanged(
                     VideoProvider videoProvider,
                     VideoProfile.CameraCapabilities cameraCapabilities) {}
 
+            /**
+             * Reports a change in the video quality received from the
+             * {@link Connection.VideoProvider} associated with a {@link RemoteConnection}.
+             *
+             * @param videoProvider The {@link RemoteConnection.VideoProvider} invoking this method.
+             * @param videoQuality  The updated peer video quality.
+             * @see InCallService.VideoCall.Callback#onVideoQualityChanged(int)
+             * @see Connection.VideoProvider#changeVideoQuality(int)
+             */
             public void onVideoQualityChanged(VideoProvider videoProvider, int videoQuality) {}
         }
 
@@ -316,14 +399,32 @@
             }
         }
 
+        /**
+         * Registers a callback to receive commands and state changes for video calls.
+         *
+         * @param l The video call callback.
+         */
         public void registerCallback(Callback l) {
             mCallbacks.add(l);
         }
 
+        /**
+         * Clears the video call callback set via {@link #registerCallback}.
+         *
+         * @param l The video call callback to clear.
+         */
         public void unregisterCallback(Callback l) {
             mCallbacks.remove(l);
         }
 
+        /**
+         * Sets the camera to be used for the outgoing video for the
+         * {@link RemoteConnection.VideoProvider}.
+         *
+         * @param cameraId The id of the camera (use ids as reported by
+         * {@link CameraManager#getCameraIdList()}).
+         * @see Connection.VideoProvider#onSetCamera(String)
+         */
         public void setCamera(String cameraId) {
             try {
                 mVideoProviderBinder.setCamera(cameraId);
@@ -331,6 +432,13 @@
             }
         }
 
+        /**
+         * Sets the surface to be used for displaying a preview of what the user's camera is
+         * currently capturing for the {@link RemoteConnection.VideoProvider}.
+         *
+         * @param surface The {@link Surface}.
+         * @see Connection.VideoProvider#onSetPreviewSurface(Surface)
+         */
         public void setPreviewSurface(Surface surface) {
             try {
                 mVideoProviderBinder.setPreviewSurface(surface);
@@ -338,6 +446,13 @@
             }
         }
 
+        /**
+         * Sets the surface to be used for displaying the video received from the remote device for
+         * the {@link RemoteConnection.VideoProvider}.
+         *
+         * @param surface The {@link Surface}.
+         * @see Connection.VideoProvider#onSetDisplaySurface(Surface)
+         */
         public void setDisplaySurface(Surface surface) {
             try {
                 mVideoProviderBinder.setDisplaySurface(surface);
@@ -345,6 +460,13 @@
             }
         }
 
+        /**
+         * Sets the device orientation, in degrees, for the {@link RemoteConnection.VideoProvider}.
+         * Assumes that a standard portrait orientation of the device is 0 degrees.
+         *
+         * @param rotation The device orientation, in degrees.
+         * @see Connection.VideoProvider#onSetDeviceOrientation(int)
+         */
         public void setDeviceOrientation(int rotation) {
             try {
                 mVideoProviderBinder.setDeviceOrientation(rotation);
@@ -352,6 +474,12 @@
             }
         }
 
+        /**
+         * Sets camera zoom ratio for the {@link RemoteConnection.VideoProvider}.
+         *
+         * @param value The camera zoom ratio.
+         * @see Connection.VideoProvider#onSetZoom(float)
+         */
         public void setZoom(float value) {
             try {
                 mVideoProviderBinder.setZoom(value);
@@ -359,6 +487,14 @@
             }
         }
 
+        /**
+         * Issues a request to modify the properties of the current video session for the
+         * {@link RemoteConnection.VideoProvider}.
+         *
+         * @param fromProfile The video profile prior to the request.
+         * @param toProfile The video profile with the requested changes made.
+         * @see Connection.VideoProvider#onSendSessionModifyRequest(VideoProfile, VideoProfile)
+         */
         public void sendSessionModifyRequest(VideoProfile fromProfile, VideoProfile toProfile) {
             try {
                 mVideoProviderBinder.sendSessionModifyRequest(fromProfile, toProfile);
@@ -366,6 +502,13 @@
             }
         }
 
+        /**
+         * Provides a response to a request to change the current call video session
+         * properties for the {@link RemoteConnection.VideoProvider}.
+         *
+         * @param responseProfile The response call video properties.
+         * @see Connection.VideoProvider#onSendSessionModifyResponse(VideoProfile)
+         */
         public void sendSessionModifyResponse(VideoProfile responseProfile) {
             try {
                 mVideoProviderBinder.sendSessionModifyResponse(responseProfile);
@@ -373,6 +516,12 @@
             }
         }
 
+        /**
+         * Issues a request to retrieve the capabilities of the current camera for the
+         * {@link RemoteConnection.VideoProvider}.
+         *
+         * @see Connection.VideoProvider#onRequestCameraCapabilities()
+         */
         public void requestCameraCapabilities() {
             try {
                 mVideoProviderBinder.requestCameraCapabilities();
@@ -380,6 +529,12 @@
             }
         }
 
+        /**
+         * Issues a request to retrieve the data usage (in bytes) of the video portion of the
+         * {@link RemoteConnection} for the {@link RemoteConnection.VideoProvider}.
+         *
+         * @see Connection.VideoProvider#onRequestConnectionDataUsage()
+         */
         public void requestCallDataUsage() {
             try {
                 mVideoProviderBinder.requestCallDataUsage();
@@ -387,6 +542,12 @@
             }
         }
 
+        /**
+         * Sets the {@link Uri} of an image to be displayed to the peer device when the video signal
+         * is paused, for the {@link RemoteConnection.VideoProvider}.
+         *
+         * @see Connection.VideoProvider#onSetPauseImage(Uri)
+         */
         public void setPauseImage(Uri uri) {
             try {
                 mVideoProviderBinder.setPauseImage(uri);
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index fb0ecb0..368e137 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -162,8 +162,8 @@
      * The extra used by a {@link ConnectionService} to provide the handle of the caller that
      * has initiated a new incoming call.
      */
-    public static final String EXTRA_INCOMING_CALL_HANDLE =
-            "android.telecom.extra.INCOMING_CALL_HANDLE";
+    public static final String EXTRA_INCOMING_CALL_ADDRESS =
+            "android.telecom.extra.INCOMING_CALL_ADDRESS";
 
     /**
      * Optional extra for {@link #ACTION_INCOMING_CALL} containing a {@link Bundle} which contains
diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java
index d192288..16472c8 100644
--- a/telephony/java/android/telephony/PhoneStateListener.java
+++ b/telephony/java/android/telephony/PhoneStateListener.java
@@ -120,8 +120,7 @@
     /**
      * Listen for changes to the device call state.
      * {@more}
-     * Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE
-     * READ_PHONE_STATE}
+     *
      * @see #onCallStateChanged
      */
     public static final int LISTEN_CALL_STATE                               = 0x00000020;
@@ -137,8 +136,6 @@
      * Listen for changes to the direction of data traffic on the data
      * connection (cellular).
      * {@more}
-     * Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE
-     * READ_PHONE_STATE}
      * Example: The status bar uses this to display the appropriate
      * data-traffic icon.
      *
@@ -388,6 +385,10 @@
 
     /**
      * Callback invoked when device call state changes.
+     * @param state call state
+     * @param incomingNumber incoming call phone number. If application does not have
+     * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} permission, an empty
+     * string will be passed as an argument.
      *
      * @see TelephonyManager#CALL_STATE_IDLE
      * @see TelephonyManager#CALL_STATE_RINGING
diff --git a/tools/aapt2/Android.mk b/tools/aapt2/Android.mk
index d311cd9..10f8150 100644
--- a/tools/aapt2/Android.mk
+++ b/tools/aapt2/Android.mk
@@ -40,6 +40,7 @@
 	ManifestParser.cpp \
 	ManifestValidator.cpp \
 	Png.cpp \
+	ProguardRules.cpp \
 	ResChunkPullParser.cpp \
 	Resource.cpp \
 	ResourceParser.cpp \
diff --git a/tools/aapt2/Main.cpp b/tools/aapt2/Main.cpp
index de2dafc..41c229d 100644
--- a/tools/aapt2/Main.cpp
+++ b/tools/aapt2/Main.cpp
@@ -28,6 +28,7 @@
 #include "ManifestValidator.h"
 #include "NameMangler.h"
 #include "Png.h"
+#include "ProguardRules.h"
 #include "ResourceParser.h"
 #include "ResourceTable.h"
 #include "ResourceTableResolver.h"
@@ -300,6 +301,9 @@
     // Directory to in which to generate R.java.
     Maybe<Source> generateJavaClass;
 
+    // File in which to produce proguard rules.
+    Maybe<Source> generateProguardRules;
+
     // Whether to output verbose details about
     // compilation.
     bool verbose = false;
@@ -417,7 +421,8 @@
 
 bool linkXml(const AaptOptions& options, const std::shared_ptr<ResourceTable>& table,
              const std::shared_ptr<IResolver>& resolver, const LinkItem& item,
-             const void* data, size_t dataLen, ZipFile* outApk, std::queue<LinkItem>* outQueue) {
+             const void* data, size_t dataLen, ZipFile* outApk, std::queue<LinkItem>* outQueue,
+             proguard::KeepSet* keepSet) {
     SourceLogger logger(item.source);
     std::unique_ptr<xml::Node> root = xml::inflate(data, dataLen, &logger);
     if (!root) {
@@ -435,6 +440,10 @@
         xmlOptions.maxSdkAttribute = item.config.sdkVersion ? item.config.sdkVersion : 1;
     }
 
+    if (options.generateProguardRules) {
+        proguard::collectProguardRules(item.name.type, item.source, root.get(), keepSet);
+    }
+
     BigBuffer outBuffer(1024);
     Maybe<size_t> minStrippedSdk = xml::flattenAndLink(item.source, root.get(),
                                                        item.originalPackage, resolver,
@@ -509,7 +518,7 @@
 
 bool compileManifest(const AaptOptions& options, const std::shared_ptr<IResolver>& resolver,
                      const std::map<std::shared_ptr<ResourceTable>, StaticLibraryData>& libApks,
-                     const android::ResTable& table, ZipFile* outApk) {
+                     const android::ResTable& table, ZipFile* outApk, proguard::KeepSet* keepSet) {
     if (options.verbose) {
         Logger::note(options.manifest) << "compiling AndroidManifest.xml." << std::endl;
     }
@@ -557,6 +566,11 @@
         }
     }
 
+    if (options.generateProguardRules) {
+        proguard::collectProguardRulesForManifest(options.manifest, merger.getMergedXml(),
+                                                  keepSet);
+    }
+
     BigBuffer outBuffer(1024);
     if (!xml::flattenAndLink(options.manifest, merger.getMergedXml(), options.appInfo.package,
                 resolver, {}, &outBuffer)) {
@@ -805,8 +819,10 @@
         return false;
     }
 
+    proguard::KeepSet keepSet;
+
     android::ResTable binTable;
-    if (!compileManifest(options, resolver, apkFiles, binTable, &outApk)) {
+    if (!compileManifest(options, resolver, apkFiles, binTable, &outApk, &keepSet)) {
         return false;
     }
 
@@ -826,7 +842,7 @@
             assert(uncompressedData);
 
             if (!linkXml(options, outTable, resolver, item, uncompressedData,
-                        entry->getUncompressedLen(), &outApk, &linkQueue)) {
+                        entry->getUncompressedLen(), &outApk, &linkQueue, &keepSet)) {
                 Logger::error(options.output) << "failed to link '" << item.originalPath << "'."
                                               << std::endl;
                 return false;
@@ -883,6 +899,26 @@
         }
     }
 
+    // Generate the Proguard rules file.
+    if (options.generateProguardRules) {
+        const Source& outPath = options.generateProguardRules.value();
+
+        if (options.verbose) {
+            Logger::note(outPath) << "writing proguard rules." << std::endl;
+        }
+
+        std::ofstream fout(outPath.path);
+        if (!fout) {
+            Logger::error(outPath) << strerror(errno) << std::endl;
+            return false;
+        }
+
+        if (!proguard::writeKeepSet(&fout, keepSet)) {
+            Logger::error(outPath) << "failed to write proguard rules." << std::endl;
+            return false;
+        }
+    }
+
     outTable->getValueStringPool().prune();
     outTable->getValueStringPool().sort(
             [](const StringPool::Entry& a, const StringPool::Entry& b) -> bool {
@@ -1072,6 +1108,11 @@
                         options.generateJavaClass = Source{ arg.toString() };
                     });
 
+            flag::optionalFlag("--proguard", "file in which to output proguard rules",
+                    [&options](const StringPiece& arg) {
+                        options.generateProguardRules = Source{ arg.toString() };
+                    });
+
             flag::optionalSwitch("--static-lib", "generate a static Android library", true,
                                  &isStaticLib);
 
diff --git a/tools/aapt2/ProguardRules.cpp b/tools/aapt2/ProguardRules.cpp
new file mode 100644
index 0000000..e89fb7c
--- /dev/null
+++ b/tools/aapt2/ProguardRules.cpp
@@ -0,0 +1,240 @@
+/*
+ * 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.
+ */
+
+#include "ProguardRules.h"
+#include "Util.h"
+#include "XmlDom.h"
+
+#include <memory>
+#include <string>
+
+namespace aapt {
+namespace proguard {
+
+constexpr const char16_t* kSchemaAndroid = u"http://schemas.android.com/apk/res/android";
+
+class BaseVisitor : public xml::Visitor {
+public:
+    BaseVisitor(const Source& source, KeepSet* keepSet) : mSource(source), mKeepSet(keepSet) {
+    }
+
+    virtual void visit(xml::Text*) override {};
+
+    virtual void visit(xml::Namespace* node) override {
+        for (const auto& child : node->children) {
+            child->accept(this);
+        }
+    }
+
+    virtual void visit(xml::Element* node) override {
+        if (!node->namespaceUri.empty()) {
+            Maybe<std::u16string> maybePackage = util::extractPackageFromNamespace(
+                    node->namespaceUri);
+            if (maybePackage) {
+                // This is a custom view, let's figure out the class name from this.
+                std::u16string package = maybePackage.value() + u"." + node->name;
+                if (util::isJavaClassName(package)) {
+                    addClass(node->lineNumber, package);
+                }
+            }
+        } else if (util::isJavaClassName(node->name)) {
+            addClass(node->lineNumber, node->name);
+        }
+
+        for (const auto& child: node->children) {
+            child->accept(this);
+        }
+    }
+
+protected:
+    void addClass(size_t lineNumber, const std::u16string& className) {
+        mKeepSet->addClass(mSource.line(lineNumber), className);
+    }
+
+    void addMethod(size_t lineNumber, const std::u16string& methodName) {
+        mKeepSet->addMethod(mSource.line(lineNumber), methodName);
+    }
+
+private:
+    Source mSource;
+    KeepSet* mKeepSet;
+};
+
+struct LayoutVisitor : public BaseVisitor {
+    LayoutVisitor(const Source& source, KeepSet* keepSet) : BaseVisitor(source, keepSet) {
+    }
+
+    virtual void visit(xml::Element* node) override {
+        bool checkClass = false;
+        bool checkName = false;
+        if (node->namespaceUri.empty()) {
+            checkClass = node->name == u"view" || node->name == u"fragment";
+        } else if (node->namespaceUri == kSchemaAndroid) {
+            checkName = node->name == u"fragment";
+        }
+
+        for (const auto& attr : node->attributes) {
+            if (checkClass && attr.namespaceUri.empty() && attr.name == u"class" &&
+                    util::isJavaClassName(attr.value)) {
+                addClass(node->lineNumber, attr.value);
+            } else if (checkName && attr.namespaceUri == kSchemaAndroid && attr.name == u"name" &&
+                    util::isJavaClassName(attr.value)) {
+                addClass(node->lineNumber, attr.value);
+            } else if (attr.namespaceUri == kSchemaAndroid && attr.name == u"onClick") {
+                addMethod(node->lineNumber, attr.value);
+            }
+        }
+
+        BaseVisitor::visit(node);
+    }
+};
+
+struct XmlResourceVisitor : public BaseVisitor {
+    XmlResourceVisitor(const Source& source, KeepSet* keepSet) : BaseVisitor(source, keepSet) {
+    }
+
+    virtual void visit(xml::Element* node) override {
+        bool checkFragment = false;
+        if (node->namespaceUri.empty()) {
+            checkFragment = node->name == u"PreferenceScreen" || node->name == u"header";
+        }
+
+        if (checkFragment) {
+            xml::Attribute* attr = node->findAttribute(kSchemaAndroid, u"fragment");
+            if (attr && util::isJavaClassName(attr->value)) {
+                addClass(node->lineNumber, attr->value);
+            }
+        }
+
+        BaseVisitor::visit(node);
+    }
+};
+
+struct TransitionVisitor : public BaseVisitor {
+    TransitionVisitor(const Source& source, KeepSet* keepSet) : BaseVisitor(source, keepSet) {
+    }
+
+    virtual void visit(xml::Element* node) override {
+        bool checkClass = node->namespaceUri.empty() &&
+                (node->name == u"transition" || node->name == u"pathMotion");
+        if (checkClass) {
+            xml::Attribute* attr = node->findAttribute({}, u"class");
+            if (attr && util::isJavaClassName(attr->value)) {
+                addClass(node->lineNumber, attr->value);
+            }
+        }
+
+        BaseVisitor::visit(node);
+    }
+};
+
+struct ManifestVisitor : public BaseVisitor {
+    ManifestVisitor(const Source& source, KeepSet* keepSet) : BaseVisitor(source, keepSet) {
+    }
+
+    virtual void visit(xml::Element* node) override {
+        if (node->namespaceUri.empty()) {
+            bool getName = false;
+            if (node->name == u"manifest") {
+                xml::Attribute* attr = node->findAttribute({}, u"package");
+                if (attr) {
+                    mPackage = attr->value;
+                }
+            } else if (node->name == u"application") {
+                getName = true;
+                xml::Attribute* attr = node->findAttribute(kSchemaAndroid, u"backupAgent");
+                if (attr) {
+                    Maybe<std::u16string> result = util::getFullyQualifiedClassName(mPackage,
+                                                                                    attr->value);
+                    if (result) {
+                        addClass(node->lineNumber, result.value());
+                    }
+                }
+            } else if (node->name == u"activity" || node->name == u"service" ||
+                    node->name == u"receiver" || node->name == u"provider" ||
+                    node->name == u"instrumentation") {
+                getName = true;
+            }
+
+            if (getName) {
+                xml::Attribute* attr = node->findAttribute(kSchemaAndroid, u"name");
+                if (attr) {
+                    Maybe<std::u16string> result = util::getFullyQualifiedClassName(mPackage,
+                                                                                    attr->value);
+                    if (result) {
+                        addClass(node->lineNumber, result.value());
+                    }
+                }
+            }
+        }
+        BaseVisitor::visit(node);
+    }
+
+    std::u16string mPackage;
+};
+
+bool collectProguardRulesForManifest(const Source& source, xml::Node* node, KeepSet* keepSet) {
+    ManifestVisitor visitor(source, keepSet);
+    node->accept(&visitor);
+    return true;
+}
+
+bool collectProguardRules(ResourceType type, const Source& source, xml::Node* node,
+                          KeepSet* keepSet) {
+    switch (type) {
+        case ResourceType::kLayout: {
+            LayoutVisitor visitor(source, keepSet);
+            node->accept(&visitor);
+            break;
+        }
+
+        case ResourceType::kXml: {
+            XmlResourceVisitor visitor(source, keepSet);
+            node->accept(&visitor);
+            break;
+        }
+
+        case ResourceType::kTransition: {
+            TransitionVisitor visitor(source, keepSet);
+            node->accept(&visitor);
+            break;
+        }
+
+        default:
+            break;
+    }
+    return true;
+}
+
+bool writeKeepSet(std::ostream* out, const KeepSet& keepSet) {
+    for (const auto& entry : keepSet.mKeepSet) {
+        for (const SourceLine& source : entry.second) {
+            *out << "// Referenced at " << source << "\n";
+        }
+        *out << "-keep class " << entry.first << " { <init>(...); }\n" << std::endl;
+    }
+
+    for (const auto& entry : keepSet.mKeepMethodSet) {
+        for (const SourceLine& source : entry.second) {
+            *out << "// Referenced at " << source << "\n";
+        }
+        *out << "-keepclassmembers class * { *** " << entry.first << "(...); }\n" << std::endl;
+    }
+    return true;
+}
+
+} // namespace proguard
+} // namespace aapt
diff --git a/tools/aapt2/ProguardRules.h b/tools/aapt2/ProguardRules.h
new file mode 100644
index 0000000..bbb3e64
--- /dev/null
+++ b/tools/aapt2/ProguardRules.h
@@ -0,0 +1,58 @@
+/*
+ * 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.
+ */
+
+#ifndef AAPT_PROGUARD_RULES_H
+#define AAPT_PROGUARD_RULES_H
+
+#include "Resource.h"
+#include "Source.h"
+#include "XmlDom.h"
+
+#include <map>
+#include <ostream>
+#include <set>
+#include <string>
+
+namespace aapt {
+namespace proguard {
+
+class KeepSet {
+public:
+    inline void addClass(const SourceLine& source, const std::u16string& className) {
+        mKeepSet[className].insert(source);
+    }
+
+    inline void addMethod(const SourceLine& source, const std::u16string& methodName) {
+        mKeepMethodSet[methodName].insert(source);
+    }
+
+private:
+    friend bool writeKeepSet(std::ostream* out, const KeepSet& keepSet);
+
+    std::map<std::u16string, std::set<SourceLine>> mKeepSet;
+    std::map<std::u16string, std::set<SourceLine>> mKeepMethodSet;
+};
+
+bool collectProguardRulesForManifest(const Source& source, xml::Node* node, KeepSet* keepSet);
+bool collectProguardRules(ResourceType type, const Source& source, xml::Node* node,
+                          KeepSet* keepSet);
+
+bool writeKeepSet(std::ostream* out, const KeepSet& keepSet);
+
+} // namespace proguard
+} // namespace aapt
+
+#endif // AAPT_PROGUARD_RULES_H
diff --git a/tools/aapt2/Source.h b/tools/aapt2/Source.h
index 10c75aa..3606488 100644
--- a/tools/aapt2/Source.h
+++ b/tools/aapt2/Source.h
@@ -19,6 +19,7 @@
 
 #include <ostream>
 #include <string>
+#include <tuple>
 
 namespace aapt {
 
@@ -80,6 +81,10 @@
     return out << source.path << ":" << source.line << ":" << source.column;
 }
 
+inline bool operator<(const SourceLine& lhs, const SourceLine& rhs) {
+    return std::tie(lhs.path, lhs.line) < std::tie(rhs.path, rhs.line);
+}
+
 } // namespace aapt
 
 #endif // AAPT_SOURCE_H
diff --git a/tools/aapt2/Util.cpp b/tools/aapt2/Util.cpp
index 7adaf1e..03ecd1a 100644
--- a/tools/aapt2/Util.cpp
+++ b/tools/aapt2/Util.cpp
@@ -102,6 +102,51 @@
     return endIter;
 }
 
+bool isJavaClassName(const StringPiece16& str) {
+    size_t pieces = 0;
+    for (const StringPiece16& piece : tokenize(str, u'.')) {
+        pieces++;
+        if (piece.empty()) {
+            return false;
+        }
+
+        // Can't have starting or trailing $ character.
+        if (piece.data()[0] == u'$' || piece.data()[piece.size() - 1] == u'$') {
+            return false;
+        }
+
+        if (findNonAlphaNumericAndNotInSet(piece, u"$_") != piece.end()) {
+            return false;
+        }
+    }
+    return pieces >= 2;
+}
+
+Maybe<std::u16string> getFullyQualifiedClassName(const StringPiece16& package,
+                                                 const StringPiece16& className) {
+    if (className.empty()) {
+        return {};
+    }
+
+    if (util::isJavaClassName(className)) {
+        return className.toString();
+    }
+
+    if (package.empty()) {
+        return {};
+    }
+
+    std::u16string result(package.data(), package.size());
+    if (className.data()[0] != u'.') {
+        result += u'.';
+    }
+    result.append(className.data(), className.size());
+    if (!isJavaClassName(result)) {
+        return {};
+    }
+    return result;
+}
+
 static Maybe<char16_t> parseUnicodeCodepoint(const char16_t** start, const char16_t* end) {
     char16_t code = 0;
     for (size_t i = 0; i < 4 && *start != end; i++, (*start)++) {
diff --git a/tools/aapt2/Util.h b/tools/aapt2/Util.h
index 6015d825..9cdb152 100644
--- a/tools/aapt2/Util.h
+++ b/tools/aapt2/Util.h
@@ -78,6 +78,23 @@
         const StringPiece16& allowedChars);
 
 /**
+ * Tests that the string is a valid Java class name.
+ */
+bool isJavaClassName(const StringPiece16& str);
+
+/**
+ * Converts the class name to a fully qualified class name from the given `package`. Ex:
+ *
+ * asdf         --> package.asdf
+ * .asdf        --> package.asdf
+ * .a.b         --> package.a.b
+ * asdf.adsf    --> asdf.adsf
+ */
+Maybe<std::u16string> getFullyQualifiedClassName(const StringPiece16& package,
+                                                 const StringPiece16& className);
+
+
+/**
  * Makes a std::unique_ptr<> with the template parameter inferred by the compiler.
  * This will be present in C++14 and can be removed then.
  */
diff --git a/tools/aapt2/Util_test.cpp b/tools/aapt2/Util_test.cpp
index c16f6bb..0b08d24 100644
--- a/tools/aapt2/Util_test.cpp
+++ b/tools/aapt2/Util_test.cpp
@@ -93,4 +93,44 @@
     ASSERT_EQ(tokenizer.end(), iter);
 }
 
+TEST(UtilTest, IsJavaClassName) {
+    EXPECT_TRUE(util::isJavaClassName(u"android.test.Class"));
+    EXPECT_TRUE(util::isJavaClassName(u"android.test.Class$Inner"));
+    EXPECT_TRUE(util::isJavaClassName(u"android_test.test.Class"));
+    EXPECT_TRUE(util::isJavaClassName(u"_android_.test._Class_"));
+    EXPECT_FALSE(util::isJavaClassName(u"android.test.$Inner"));
+    EXPECT_FALSE(util::isJavaClassName(u"android.test.Inner$"));
+    EXPECT_FALSE(util::isJavaClassName(u".test.Class"));
+    EXPECT_FALSE(util::isJavaClassName(u"android"));
+}
+
+TEST(UtilTest, FullyQualifiedClassName) {
+    Maybe<std::u16string> res = util::getFullyQualifiedClassName(u"android", u"asdf");
+    ASSERT_TRUE(res);
+    EXPECT_EQ(res.value(), u"android.asdf");
+
+    res = util::getFullyQualifiedClassName(u"android", u".asdf");
+    ASSERT_TRUE(res);
+    EXPECT_EQ(res.value(), u"android.asdf");
+
+    res = util::getFullyQualifiedClassName(u"android", u".a.b");
+    ASSERT_TRUE(res);
+    EXPECT_EQ(res.value(), u"android.a.b");
+
+    res = util::getFullyQualifiedClassName(u"android", u"a.b");
+    ASSERT_TRUE(res);
+    EXPECT_EQ(res.value(), u"a.b");
+
+    res = util::getFullyQualifiedClassName(u"", u"a.b");
+    ASSERT_TRUE(res);
+    EXPECT_EQ(res.value(), u"a.b");
+
+    res = util::getFullyQualifiedClassName(u"", u"");
+    ASSERT_FALSE(res);
+
+    res = util::getFullyQualifiedClassName(u"android", u"./Apple");
+    ASSERT_FALSE(res);
+}
+
+
 } // namespace aapt
diff --git a/tools/aapt2/data/AndroidManifest.xml b/tools/aapt2/data/AndroidManifest.xml
index c017a0d..8533c28 100644
--- a/tools/aapt2/data/AndroidManifest.xml
+++ b/tools/aapt2/data/AndroidManifest.xml
@@ -1,6 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.android.app">
-    <application>
+    <application
+        android:name=".Activity">
     </application>
 </manifest>
diff --git a/tools/aapt2/data/Makefile b/tools/aapt2/data/Makefile
index ce5201b..3387135 100644
--- a/tools/aapt2/data/Makefile
+++ b/tools/aapt2/data/Makefile
@@ -15,6 +15,7 @@
 LOCAL_LIBS := lib/out/package.apk
 LOCAL_OUT := out
 LOCAL_GEN := out/gen
+LOCAL_PROGUARD := out/proguard.rule
 
 ##
 # AAPT2 custom rules.
@@ -57,7 +58,7 @@
 
 # Link: out/package-unaligned.apk <- out/values-v4.apk out/drawable-v4.apk
 $(PRIVATE_APK_UNALIGNED): $(PRIVATE_INTERMEDIATE_TABLES) $(PRIVATE_INCLUDES) $(LOCAL_LIBS) AndroidManifest.xml
-	$(AAPT) link --manifest AndroidManifest.xml $(addprefix -I ,$(PRIVATE_INCLUDES)) --java $(LOCAL_GEN) -o $@ $(PRIVATE_INTERMEDIATE_TABLES) $(LOCAL_LIBS)
+	$(AAPT) link --manifest AndroidManifest.xml $(addprefix -I ,$(PRIVATE_INCLUDES)) --java $(LOCAL_GEN) -o $@ $(PRIVATE_INTERMEDIATE_TABLES) $(LOCAL_LIBS) --proguard $(LOCAL_PROGUARD) -v
 
 # R.java: gen/com/android/app/R.java <- out/resources.arsc
 # No action since R.java is generated when out/resources.arsc is.
diff --git a/tools/aapt2/data/res/layout/main.xml b/tools/aapt2/data/res/layout/main.xml
index 77ccedb..50a51d9 100644
--- a/tools/aapt2/data/res/layout/main.xml
+++ b/tools/aapt2/data/res/layout/main.xml
@@ -5,11 +5,14 @@
     android:layout_width="match_parent"
     android:layout_height="wrap_content">
 
+    <fragment class="android.test.sample.App$Inner" />
+
     <variable name="user" type="com.android.User" />
 
     <View xmlns:app="http://schemas.android.com/apk/res-auto"
         android:id="@+id/me"
         android:layout_width="1dp"
+        android:onClick="doClick"
         android:text="@{user.name}"
         android:layout_height="match_parent"
         app:layout_width="@support:bool/allow"