Merge "Make sure tap detector is valid before trying to initialize."
diff --git a/api/current.txt b/api/current.txt
index 59b8fb3..d54b7fd 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -7441,12 +7441,16 @@
     method public static boolean compareMimeTypes(java.lang.String, java.lang.String);
     method public int describeContents();
     method public java.lang.String[] filterMimeTypes(java.lang.String);
+    method public android.os.PersistableBundle getExtras();
     method public java.lang.CharSequence getLabel();
     method public java.lang.String getMimeType(int);
     method public int getMimeTypeCount();
     method public boolean hasMimeType(java.lang.String);
+    method public void setExtras(android.os.PersistableBundle);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.content.ClipDescription> CREATOR;
+    field public static final java.lang.String EXTRA_TARGET_COMPONENT_NAME = "android.content.extra.TARGET_COMPONENT_NAME";
+    field public static final java.lang.String EXTRA_USER_SERIAL_NUMBER = "android.content.extra.USER_SERIAL_NUMBER";
     field public static final java.lang.String MIMETYPE_TEXT_HTML = "text/html";
     field public static final java.lang.String MIMETYPE_TEXT_INTENT = "text/vnd.android.intent";
     field public static final java.lang.String MIMETYPE_TEXT_PLAIN = "text/plain";
@@ -7908,6 +7912,7 @@
     field public static final int MODE_APPEND = 32768; // 0x8000
     field public static final int MODE_ENABLE_WRITE_AHEAD_LOGGING = 8; // 0x8
     field public static final deprecated int MODE_MULTI_PROCESS = 4; // 0x4
+    field public static final int MODE_NO_LOCALIZED_COLLATORS = 16; // 0x10
     field public static final int MODE_PRIVATE = 0; // 0x0
     field public static final deprecated int MODE_WORLD_READABLE = 1; // 0x1
     field public static final deprecated int MODE_WORLD_WRITEABLE = 2; // 0x2
@@ -28060,6 +28065,7 @@
     method public boolean isUserAGoat();
     method public boolean isUserRunning(android.os.UserHandle);
     method public boolean isUserRunningOrStopping(android.os.UserHandle);
+    method public boolean isUserRunningUnlocked(android.os.UserHandle);
     method public deprecated boolean setRestrictionsChallenge(java.lang.String);
     method public deprecated void setUserRestriction(java.lang.String, boolean);
     method public deprecated void setUserRestrictions(android.os.Bundle);
@@ -28864,6 +28870,8 @@
     method public boolean isFailed();
     method public boolean isQueued();
     method public boolean isStarted();
+    method public void setProgress(float);
+    method public void setStatus(java.lang.CharSequence);
     method public boolean setTag(java.lang.String);
     method public boolean start();
   }
diff --git a/api/system-current.txt b/api/system-current.txt
index f029568..2ea5ebb 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -100,6 +100,7 @@
     field public static final java.lang.String GET_APP_OPS_STATS = "android.permission.GET_APP_OPS_STATS";
     field public static final java.lang.String GET_PACKAGE_IMPORTANCE = "android.permission.GET_PACKAGE_IMPORTANCE";
     field public static final java.lang.String GET_PACKAGE_SIZE = "android.permission.GET_PACKAGE_SIZE";
+    field public static final java.lang.String GET_PROCESS_STATE_AND_OOM_SCORE = "android.permission.GET_PROCESS_STATE_AND_OOM_SCORE";
     field public static final deprecated java.lang.String GET_TASKS = "android.permission.GET_TASKS";
     field public static final java.lang.String GET_TOP_ACTIVITY_INFO = "android.permission.GET_TOP_ACTIVITY_INFO";
     field public static final java.lang.String GLOBAL_SEARCH = "android.permission.GLOBAL_SEARCH";
@@ -7682,12 +7683,16 @@
     method public static boolean compareMimeTypes(java.lang.String, java.lang.String);
     method public int describeContents();
     method public java.lang.String[] filterMimeTypes(java.lang.String);
+    method public android.os.PersistableBundle getExtras();
     method public java.lang.CharSequence getLabel();
     method public java.lang.String getMimeType(int);
     method public int getMimeTypeCount();
     method public boolean hasMimeType(java.lang.String);
+    method public void setExtras(android.os.PersistableBundle);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.content.ClipDescription> CREATOR;
+    field public static final java.lang.String EXTRA_TARGET_COMPONENT_NAME = "android.content.extra.TARGET_COMPONENT_NAME";
+    field public static final java.lang.String EXTRA_USER_SERIAL_NUMBER = "android.content.extra.USER_SERIAL_NUMBER";
     field public static final java.lang.String MIMETYPE_TEXT_HTML = "text/html";
     field public static final java.lang.String MIMETYPE_TEXT_INTENT = "text/vnd.android.intent";
     field public static final java.lang.String MIMETYPE_TEXT_PLAIN = "text/plain";
@@ -8155,6 +8160,7 @@
     field public static final int MODE_APPEND = 32768; // 0x8000
     field public static final int MODE_ENABLE_WRITE_AHEAD_LOGGING = 8; // 0x8
     field public static final deprecated int MODE_MULTI_PROCESS = 4; // 0x4
+    field public static final int MODE_NO_LOCALIZED_COLLATORS = 16; // 0x10
     field public static final int MODE_PRIVATE = 0; // 0x0
     field public static final deprecated int MODE_WORLD_READABLE = 1; // 0x1
     field public static final deprecated int MODE_WORLD_WRITEABLE = 2; // 0x2
@@ -30043,6 +30049,7 @@
     method public boolean isUserAGoat();
     method public boolean isUserRunning(android.os.UserHandle);
     method public boolean isUserRunningOrStopping(android.os.UserHandle);
+    method public boolean isUserRunningUnlocked(android.os.UserHandle);
     method public deprecated boolean setRestrictionsChallenge(java.lang.String);
     method public deprecated void setUserRestriction(java.lang.String, boolean);
     method public deprecated void setUserRestrictions(android.os.Bundle);
@@ -30847,6 +30854,8 @@
     method public boolean isFailed();
     method public boolean isQueued();
     method public boolean isStarted();
+    method public void setProgress(float);
+    method public void setStatus(java.lang.CharSequence);
     method public boolean setTag(java.lang.String);
     method public boolean start();
   }
diff --git a/api/test-current.txt b/api/test-current.txt
index 59b8fb3..904347d 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -7441,12 +7441,16 @@
     method public static boolean compareMimeTypes(java.lang.String, java.lang.String);
     method public int describeContents();
     method public java.lang.String[] filterMimeTypes(java.lang.String);
+    method public android.os.PersistableBundle getExtras();
     method public java.lang.CharSequence getLabel();
     method public java.lang.String getMimeType(int);
     method public int getMimeTypeCount();
     method public boolean hasMimeType(java.lang.String);
+    method public void setExtras(android.os.PersistableBundle);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.content.ClipDescription> CREATOR;
+    field public static final java.lang.String EXTRA_TARGET_COMPONENT_NAME = "android.content.extra.TARGET_COMPONENT_NAME";
+    field public static final java.lang.String EXTRA_USER_SERIAL_NUMBER = "android.content.extra.USER_SERIAL_NUMBER";
     field public static final java.lang.String MIMETYPE_TEXT_HTML = "text/html";
     field public static final java.lang.String MIMETYPE_TEXT_INTENT = "text/vnd.android.intent";
     field public static final java.lang.String MIMETYPE_TEXT_PLAIN = "text/plain";
@@ -7908,6 +7912,7 @@
     field public static final int MODE_APPEND = 32768; // 0x8000
     field public static final int MODE_ENABLE_WRITE_AHEAD_LOGGING = 8; // 0x8
     field public static final deprecated int MODE_MULTI_PROCESS = 4; // 0x4
+    field public static final int MODE_NO_LOCALIZED_COLLATORS = 16; // 0x10
     field public static final int MODE_PRIVATE = 0; // 0x0
     field public static final deprecated int MODE_WORLD_READABLE = 1; // 0x1
     field public static final deprecated int MODE_WORLD_WRITEABLE = 2; // 0x2
@@ -28060,6 +28065,7 @@
     method public boolean isUserAGoat();
     method public boolean isUserRunning(android.os.UserHandle);
     method public boolean isUserRunningOrStopping(android.os.UserHandle);
+    method public boolean isUserRunningUnlocked(android.os.UserHandle);
     method public deprecated boolean setRestrictionsChallenge(java.lang.String);
     method public deprecated void setUserRestriction(java.lang.String, boolean);
     method public deprecated void setUserRestrictions(android.os.Bundle);
@@ -28745,7 +28751,9 @@
     method public java.lang.String getLabel();
     method public android.print.PageRange[] getPages();
     method public android.print.PrinterId getPrinterId();
+    method public float getProgress();
     method public int getState();
+    method public java.lang.CharSequence getStatus();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.print.PrintJobInfo> CREATOR;
     field public static final int STATE_BLOCKED = 4; // 0x4
@@ -28864,6 +28872,8 @@
     method public boolean isFailed();
     method public boolean isQueued();
     method public boolean isStarted();
+    method public void setProgress(float);
+    method public void setStatus(java.lang.CharSequence);
     method public boolean setTag(java.lang.String);
     method public boolean start();
   }
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index daf01ec..2ad63b5 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -154,7 +154,7 @@
                 "       am switch-user <USER_ID>\n" +
                 "       am start-user <USER_ID>\n" +
                 "       am unlock-user <USER_ID> [TOKEN_HEX]\n" +
-                "       am stop-user [-w] <USER_ID>\n" +
+                "       am stop-user [-w] [-f] <USER_ID>\n" +
                 "       am stack start <DISPLAY_ID> <INTENT>\n" +
                 "       am stack movetask <TASK_ID> <STACK_ID> [true|false]\n" +
                 "       am stack resize <STACK_ID> <LEFT,TOP,RIGHT,BOTTOM>\n" +
@@ -290,6 +290,7 @@
                 "am stop-user: stop execution of USER_ID, not allowing it to run any\n" +
                 "  code until a later explicit start or switch to it.\n" +
                 "  -w: wait for stop-user to complete.\n" +
+                "  -f: force stop even if there are related users that cannot be stopped.\n" +
                 "\n" +
                 "am stack start: start a new activity on <DISPLAY_ID> using <INTENT>.\n" +
                 "\n" +
@@ -1131,10 +1132,13 @@
 
     private void runStopUser() throws Exception {
         boolean wait = false;
-        String opt = null;
+        boolean force = false;
+        String opt;
         while ((opt = nextOption()) != null) {
             if ("-w".equals(opt)) {
                 wait = true;
+            } else if ("-f".equals(opt)) {
+                force = true;
             } else {
                 System.err.println("Error: unknown option: " + opt);
                 return;
@@ -1143,7 +1147,7 @@
         int user = Integer.parseInt(nextArgRequired());
         StopUserCallback callback = wait ? new StopUserCallback() : null;
 
-        int res = mAm.stopUser(user, callback);
+        int res = mAm.stopUser(user, force, callback);
         if (res != ActivityManager.USER_OP_SUCCESS) {
             String txt = "";
             switch (res) {
@@ -1153,6 +1157,13 @@
                 case ActivityManager.USER_OP_UNKNOWN_USER:
                     txt = " (Unknown user " + user + ")";
                     break;
+                case ActivityManager.USER_OP_ERROR_IS_SYSTEM:
+                    txt = " (System user cannot be stopped)";
+                    break;
+                case ActivityManager.USER_OP_ERROR_RELATED_USERS_CANNOT_STOP:
+                    txt = " (Can't stop user " + user
+                            + " - one of its related users can't be stopped)";
+                    break;
             }
             System.err.println("Switch failed: " + res + txt);
         } else if (callback != null) {
diff --git a/cmds/settings/src/com/android/commands/settings/SettingsCmd.java b/cmds/settings/src/com/android/commands/settings/SettingsCmd.java
index 726167e..e63a1f5 100644
--- a/cmds/settings/src/com/android/commands/settings/SettingsCmd.java
+++ b/cmds/settings/src/com/android/commands/settings/SettingsCmd.java
@@ -76,7 +76,12 @@
                         // --user specified more than once; invalid
                         break;
                     }
-                    mUser = Integer.parseInt(nextArg());
+                    arg = nextArg();
+                    if ("current".equals(arg) || "cur".equals(arg)) {
+                        mUser = UserHandle.USER_CURRENT;
+                    } else {
+                        mUser = Integer.parseInt(arg);
+                    }
                 } else if (mVerb == CommandVerb.UNSPECIFIED) {
                     if ("get".equalsIgnoreCase(arg)) {
                         mVerb = CommandVerb.GET;
@@ -129,12 +134,14 @@
         }
 
         if (valid) {
-            if (mUser < 0) {
-                mUser = UserHandle.USER_SYSTEM;
-            }
-
             try {
                 IActivityManager activityManager = ActivityManagerNative.getDefault();
+                if (mUser == UserHandle.USER_CURRENT) {
+                    mUser = activityManager.getCurrentUser().id;
+                }
+                if (mUser < 0) {
+                    mUser = UserHandle.USER_SYSTEM;
+                }
                 IContentProvider provider = null;
                 IBinder token = new Binder();
                 try {
@@ -286,13 +293,13 @@
     }
 
     private static void printUsage() {
-        System.err.println("usage:  settings [--user NUM] get namespace key");
-        System.err.println("        settings [--user NUM] put namespace key value");
-        System.err.println("        settings [--user NUM] delete namespace key");
-        System.err.println("        settings [--user NUM] list namespace");
+        System.err.println("usage:  settings [--user <USER_ID> | current] get namespace key");
+        System.err.println("        settings [--user <USER_ID> | current] put namespace key value");
+        System.err.println("        settings [--user <USER_ID> | current] delete namespace key");
+        System.err.println("        settings [--user <USER_ID> | current] list namespace");
         System.err.println("\n'namespace' is one of {system, secure, global}, case-insensitive");
-        System.err.println("If '--user NUM' is not given, the operations are performed on the "
-                + "system user.");
+        System.err.println("If '--user <USER_ID> | current' is not given, the operations are "
+                + "performed on the system user.");
     }
 
     public static String resolveCallingPackage() {
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index f1a7de8..c203854 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -265,6 +265,12 @@
     /** @hide User operation call: given user id is the current user, can't be stopped. */
     public static final int USER_OP_IS_CURRENT = -2;
 
+    /** @hide User operation call: system user can't be stopped. */
+    public static final int USER_OP_ERROR_IS_SYSTEM = -3;
+
+    /** @hide User operation call: one of related users cannot be stopped. */
+    public static final int USER_OP_ERROR_RELATED_USERS_CANNOT_STOP = -4;
+
     /** @hide Process does not exist. */
     public static final int PROCESS_STATE_NONEXISTENT = -1;
 
@@ -537,6 +543,11 @@
             return stackId == FREEFORM_WORKSPACE_STACK_ID
                     || stackId == FULLSCREEN_WORKSPACE_STACK_ID || stackId == DOCKED_STACK_ID;
         }
+
+        /** Returns true if the windows in the stack can receive input keys. */
+        public static boolean canReceiveKeys(int stackId) {
+            return stackId != PINNED_STACK_ID;
+        }
     }
 
     /**
@@ -3085,6 +3096,8 @@
     public static final int FLAG_OR_STOPPED = 1 << 0;
     /** {@hide} */
     public static final int FLAG_AND_LOCKED = 1 << 1;
+    /** {@hide} */
+    public static final int FLAG_AND_UNLOCKED = 1 << 2;
 
     /**
      * Return whether the given user is actively running.  This means that
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 19d9fc2..c05d5e8 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -1981,9 +1981,10 @@
         case STOP_USER_TRANSACTION: {
             data.enforceInterface(IActivityManager.descriptor);
             int userid = data.readInt();
+            boolean force = data.readInt() != 0;
             IStopUserCallback callback = IStopUserCallback.Stub.asInterface(
                     data.readStrongBinder());
-            int result = stopUser(userid, callback);
+            int result = stopUser(userid, force, callback);
             reply.writeNoException();
             reply.writeInt(result);
             return true;
@@ -5287,11 +5288,13 @@
         return result;
     }
 
-    public int stopUser(int userid, IStopUserCallback callback) throws RemoteException {
+    public int stopUser(int userid, boolean force, IStopUserCallback callback)
+            throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
         data.writeInterfaceToken(IActivityManager.descriptor);
         data.writeInt(userid);
+        data.writeInt(force ? 1 : 0);
         data.writeStrongInterface(callback);
         mRemote.transact(STOP_USER_TRANSACTION, data, reply, 0);
         reply.readException();
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 802880d..74f0c0e 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -92,7 +92,7 @@
 import android.util.SparseIntArray;
 import android.util.SuperNotCalledException;
 import android.view.Display;
-import android.view.HardwareRenderer;
+import android.view.ThreadedRenderer;
 import android.view.View;
 import android.view.ViewDebug;
 import android.view.ViewManager;
@@ -4612,7 +4612,7 @@
             // If there are several packages in this application we won't
             // initialize the graphics disk caches
             if (packages != null && packages.length == 1) {
-                HardwareRenderer.setupDiskCache(cacheDir);
+                ThreadedRenderer.setupDiskCache(cacheDir);
                 RenderScriptCacheDir.setupDiskCache(cacheDir);
             }
         } catch (RemoteException e) {
@@ -4659,7 +4659,7 @@
             // use hardware accelerated drawing, since this can add too much
             // overhead to the process.
             if (!ActivityManager.isHighEndGfx()) {
-                HardwareRenderer.disable(false);
+                ThreadedRenderer.disable(false);
             }
         }
 
@@ -5529,9 +5529,9 @@
         // accelerated drawing, since this can add too much overhead to the
         // process.
         if (!ActivityManager.isHighEndGfx()) {
-            HardwareRenderer.disable(true);
+            ThreadedRenderer.disable(true);
         } else {
-            HardwareRenderer.enableForegroundTrimming();
+            ThreadedRenderer.enableForegroundTrimming();
         }
         ActivityThread thread = new ActivityThread();
         thread.attach(true);
diff --git a/core/java/android/app/AlarmManager.java b/core/java/android/app/AlarmManager.java
index bf2e13a..b569416 100644
--- a/core/java/android/app/AlarmManager.java
+++ b/core/java/android/app/AlarmManager.java
@@ -25,7 +25,6 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.RemoteException;
-import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.WorkSource;
 import android.text.TextUtils;
@@ -869,13 +868,19 @@
      * {@link Intent#filterEquals}), will be canceled.
      *
      * @param operation IntentSender which matches a previously added
-     * IntentSender.
+     * IntentSender. This parameter must not be {@code null}.
      *
      * @see #set
      */
     public void cancel(PendingIntent operation) {
         if (operation == null) {
-            throw new NullPointerException("operation");
+            final String msg = "cancel() called with a null PendingIntent";
+            if (mTargetSdkVersion >= Build.VERSION_CODES.N) {
+                throw new NullPointerException(msg);
+            } else {
+                Log.e(TAG, msg);
+                return;
+            }
         }
 
         try {
@@ -891,7 +896,7 @@
      */
     public void cancel(OnAlarmListener listener) {
         if (listener == null) {
-            throw new NullPointerException("listener");
+            throw new NullPointerException("cancel() called with a null OnAlarmListener");
         }
 
         ListenerWrapper wrapper = null;
diff --git a/core/java/android/app/BackStackRecord.java b/core/java/android/app/BackStackRecord.java
index 9081ef8..b24bce3 100644
--- a/core/java/android/app/BackStackRecord.java
+++ b/core/java/android/app/BackStackRecord.java
@@ -798,21 +798,33 @@
         }
     }
 
-    private static void setFirstOut(SparseArray<Fragment> fragments, Fragment fragment) {
+    private static void setFirstOut(SparseArray<Fragment> firstOutFragments,
+                            SparseArray<Fragment> lastInFragments, Fragment fragment) {
         if (fragment != null) {
             int containerId = fragment.mContainerId;
-            if (containerId != 0 && !fragment.isHidden() && fragment.isAdded() &&
-                    fragment.getView() != null && fragments.get(containerId) == null) {
-                fragments.put(containerId, fragment);
+            if (containerId != 0 && !fragment.isHidden()) {
+                if (fragment.isAdded() && fragment.getView() != null
+                        && firstOutFragments.get(containerId) == null) {
+                    firstOutFragments.put(containerId, fragment);
+                }
+                if (lastInFragments.get(containerId) == fragment) {
+                    lastInFragments.remove(containerId);
+                }
             }
         }
     }
 
-    private void setLastIn(SparseArray<Fragment> fragments, Fragment fragment) {
+    private void setLastIn(SparseArray<Fragment> firstOutFragments,
+            SparseArray<Fragment> lastInFragments, Fragment fragment) {
         if (fragment != null) {
             int containerId = fragment.mContainerId;
             if (containerId != 0) {
-                fragments.put(containerId, fragment);
+                if (!fragment.isAdded()) {
+                    lastInFragments.put(containerId, fragment);
+                }
+                if (firstOutFragments.get(containerId) == fragment) {
+                    firstOutFragments.remove(containerId);
+                }
             }
         }
     }
@@ -835,7 +847,7 @@
         while (op != null) {
             switch (op.cmd) {
                 case OP_ADD:
-                    setLastIn(lastInFragments, op.fragment);
+                    setLastIn(firstOutFragments, lastInFragments, op.fragment);
                     break;
                 case OP_REPLACE: {
                     Fragment f = op.fragment;
@@ -845,29 +857,30 @@
                             if (f == null || old.mContainerId == f.mContainerId) {
                                 if (old == f) {
                                     f = null;
+                                    lastInFragments.remove(old.mContainerId);
                                 } else {
-                                    setFirstOut(firstOutFragments, old);
+                                    setFirstOut(firstOutFragments, lastInFragments, old);
                                 }
                             }
                         }
                     }
-                    setLastIn(lastInFragments, f);
+                    setLastIn(firstOutFragments, lastInFragments, op.fragment);
                     break;
                 }
                 case OP_REMOVE:
-                    setFirstOut(firstOutFragments, op.fragment);
+                    setFirstOut(firstOutFragments, lastInFragments, op.fragment);
                     break;
                 case OP_HIDE:
-                    setFirstOut(firstOutFragments, op.fragment);
+                    setFirstOut(firstOutFragments, lastInFragments, op.fragment);
                     break;
                 case OP_SHOW:
-                    setLastIn(lastInFragments, op.fragment);
+                    setLastIn(firstOutFragments, lastInFragments, op.fragment);
                     break;
                 case OP_DETACH:
-                    setFirstOut(firstOutFragments, op.fragment);
+                    setFirstOut(firstOutFragments, lastInFragments, op.fragment);
                     break;
                 case OP_ATTACH:
-                    setLastIn(lastInFragments, op.fragment);
+                    setLastIn(firstOutFragments, lastInFragments, op.fragment);
                     break;
             }
 
@@ -889,38 +902,38 @@
         if (!mManager.mContainer.onHasView()) {
             return; // nothing to see, so no transitions
         }
-        Op op = mHead;
+        Op op = mTail;
         while (op != null) {
             switch (op.cmd) {
                 case OP_ADD:
-                    setFirstOut(firstOutFragments, op.fragment);
+                    setFirstOut(firstOutFragments, lastInFragments, op.fragment);
                     break;
                 case OP_REPLACE:
                     if (op.removed != null) {
                         for (int i = op.removed.size() - 1; i >= 0; i--) {
-                            setLastIn(lastInFragments, op.removed.get(i));
+                            setLastIn(firstOutFragments, lastInFragments, op.removed.get(i));
                         }
                     }
-                    setFirstOut(firstOutFragments, op.fragment);
+                    setFirstOut(firstOutFragments, lastInFragments, op.fragment);
                     break;
                 case OP_REMOVE:
-                    setLastIn(lastInFragments, op.fragment);
+                    setLastIn(firstOutFragments, lastInFragments, op.fragment);
                     break;
                 case OP_HIDE:
-                    setLastIn(lastInFragments, op.fragment);
+                    setLastIn(firstOutFragments, lastInFragments, op.fragment);
                     break;
                 case OP_SHOW:
-                    setFirstOut(firstOutFragments, op.fragment);
+                    setFirstOut(firstOutFragments, lastInFragments, op.fragment);
                     break;
                 case OP_DETACH:
-                    setLastIn(lastInFragments, op.fragment);
+                    setLastIn(firstOutFragments, lastInFragments, op.fragment);
                     break;
                 case OP_ATTACH:
-                    setFirstOut(firstOutFragments, op.fragment);
+                    setFirstOut(firstOutFragments, lastInFragments, op.fragment);
                     break;
             }
 
-            op = op.next;
+            op = op.prev;
         }
     }
 
@@ -957,6 +970,7 @@
      */
     private TransitionState beginTransition(SparseArray<Fragment> firstOutFragments,
             SparseArray<Fragment> lastInFragments, boolean isBack) {
+        ensureFragmentsAreInitialized(lastInFragments);
         TransitionState state = new TransitionState();
 
         // Adding a non-existent target view makes sure that the transitions don't target
@@ -982,6 +996,20 @@
         return state;
     }
 
+    /**
+     * Ensure that fragments that are entering are at least at the CREATED state
+     * so that they may load Transitions using TransitionInflater.
+     */
+    private void ensureFragmentsAreInitialized(SparseArray<Fragment> lastInFragments) {
+        final int count = lastInFragments.size();
+        for (int i = 0; i < count; i++) {
+            final Fragment fragment = lastInFragments.valueAt(i);
+            if (fragment.mState < Fragment.CREATED) {
+                mManager.moveToState(fragment, Fragment.CREATED, 0, 0, false);
+            }
+        }
+    }
+
     private static Transition cloneTransition(Transition transition) {
         if (transition != null) {
             transition = transition.clone();
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index c661107..23c4198 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -589,6 +589,9 @@
         if ((mode & MODE_ENABLE_WRITE_AHEAD_LOGGING) != 0) {
             flags |= SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING;
         }
+        if ((mode & MODE_NO_LOCALIZED_COLLATORS) != 0) {
+            flags |= SQLiteDatabase.NO_LOCALIZED_COLLATORS;
+        }
         SQLiteDatabase db = SQLiteDatabase.openDatabase(f.getPath(), factory, flags, errorHandler);
         setFilePermissionsFromMode(f.getPath(), mode, 0);
         return db;
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 09c6c0b..38c7957 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -391,7 +391,7 @@
     public boolean switchUser(int userid) throws RemoteException;
     public boolean startUserInBackground(int userid) throws RemoteException;
     public boolean unlockUser(int userid, byte[] token) throws RemoteException;
-    public int stopUser(int userid, IStopUserCallback callback) throws RemoteException;
+    public int stopUser(int userid, boolean force, IStopUserCallback callback) throws RemoteException;
     public UserInfo getCurrentUser() throws RemoteException;
     public boolean isUserRunning(int userid, int flags) throws RemoteException;
     public int[] getRunningUserIds() throws RemoteException;
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index c1d5b19..c504ce3 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -93,6 +93,7 @@
     AutomaticZenRule addAutomaticZenRule(in AutomaticZenRule automaticZenRule);
     boolean updateAutomaticZenRule(in AutomaticZenRule automaticZenRule);
     boolean removeAutomaticZenRule(String id);
+    boolean removeAutomaticZenRules(String packageName);
 
     byte[] getBackupPayload(int user);
     void applyRestore(in byte[] payload, int user);
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 3eb3e0f..89610e9 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -470,6 +470,20 @@
     }
 
     /**
+     * Deletes all automatic zen rules owned by the given package.
+     *
+     * @hide
+     */
+    public boolean removeAutomaticZenRules(String packageName) {
+        INotificationManager service = getService();
+        try {
+            return service.removeAutomaticZenRules(packageName);
+        } catch (RemoteException e) {
+        }
+        return false;
+    }
+
+    /**
      * Checks the ability to read/modify notification policy for the calling package.
      *
      * <p>
diff --git a/core/java/android/content/ClipDescription.java b/core/java/android/content/ClipDescription.java
index e988516..1b024e2 100644
--- a/core/java/android/content/ClipDescription.java
+++ b/core/java/android/content/ClipDescription.java
@@ -18,6 +18,7 @@
 
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.PersistableBundle;
 import android.text.TextUtils;
 
 import java.util.ArrayList;
@@ -59,8 +60,35 @@
      */
     public static final String MIMETYPE_TEXT_INTENT = "text/vnd.android.intent";
 
+    /**
+     * The name of the extra used to define a component name when copying/dragging
+     * an app icon from Launcher.
+     * <p>
+     * Type: String
+     * </p>
+     * <p>
+     * Use {@link ComponentName#unflattenFromString(String)}
+     * and {@link ComponentName#flattenToString()} to convert the extra value
+     * to/from {@link ComponentName}.
+     * </p>
+     */
+    public static final String EXTRA_TARGET_COMPONENT_NAME =
+            "android.content.extra.TARGET_COMPONENT_NAME";
+
+    /**
+     * The name of the extra used to define a user serial number when copying/dragging
+     * an app icon from Launcher.
+     * <p>
+     * Type: long
+     * </p>
+     */
+    public static final String EXTRA_USER_SERIAL_NUMBER =
+            "android.content.extra.USER_SERIAL_NUMBER";
+
+
     final CharSequence mLabel;
     final String[] mMimeTypes;
+    private PersistableBundle mExtras;
 
     /**
      * Create a new clip.
@@ -173,6 +201,27 @@
         return mMimeTypes[index];
     }
 
+    /**
+     * Retrieve extended data from the clip description.
+     *
+     * @return the bundle containing extended data previously set with
+     * {@link #setExtras(PersistableBundle)}, or null if no extras have been set.
+     *
+     * @see #setExtras(PersistableBundle)
+     */
+    public PersistableBundle getExtras() {
+        return mExtras;
+    }
+
+    /**
+     * Add extended data to the clip description.
+     *
+     * @see #getExtras()
+     */
+    public void setExtras(PersistableBundle extras) {
+        mExtras = new PersistableBundle(extras);
+    }
+
     /** @hide */
     public void validate() {
         if (mMimeTypes == null) {
@@ -211,6 +260,13 @@
             b.append(mLabel);
             b.append('"');
         }
+        if (mExtras != null) {
+            if (!first) {
+                b.append(' ');
+            }
+            first = false;
+            b.append(mExtras.toString());
+        }
         return !first;
     }
 
@@ -236,11 +292,13 @@
     public void writeToParcel(Parcel dest, int flags) {
         TextUtils.writeToParcel(mLabel, dest, flags);
         dest.writeStringArray(mMimeTypes);
+        dest.writePersistableBundle(mExtras);
     }
 
     ClipDescription(Parcel in) {
         mLabel = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
         mMimeTypes = in.createStringArray();
+        mExtras = in.readPersistableBundle();
     }
 
     public static final Parcelable.Creator<ClipDescription> CREATOR =
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index b73fa50..6cc5497 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -159,6 +159,16 @@
      */
     public static final int MODE_ENABLE_WRITE_AHEAD_LOGGING = 0x0008;
 
+    /**
+     * Database open flag: when set, the database is opened without support for
+     * localized collators.
+     *
+     * @see #openOrCreateDatabase(String, int, CursorFactory)
+     * @see #openOrCreateDatabase(String, int, CursorFactory, DatabaseErrorHandler)
+     * @see SQLiteDatabase#NO_LOCALIZED_COLLATORS
+     */
+    public static final int MODE_NO_LOCALIZED_COLLATORS = 0x0010;
+
     /** @hide */
     @IntDef(flag = true,
             value = {
@@ -1271,6 +1281,7 @@
      *     default operation, {@link #MODE_WORLD_READABLE}
      *     and {@link #MODE_WORLD_WRITEABLE} to control permissions.
      *     Use {@link #MODE_ENABLE_WRITE_AHEAD_LOGGING} to enable write-ahead logging by default.
+     *     Use {@link #MODE_NO_LOCALIZED_COLLATORS} to disable localized collators.
      * @param factory An optional factory class that is called to instantiate a
      *     cursor when query is called.
      *
@@ -1281,6 +1292,7 @@
      * @see #MODE_WORLD_READABLE
      * @see #MODE_WORLD_WRITEABLE
      * @see #MODE_ENABLE_WRITE_AHEAD_LOGGING
+     * @see #MODE_NO_LOCALIZED_COLLATORS
      * @see #deleteDatabase
      */
     public abstract SQLiteDatabase openOrCreateDatabase(String name,
@@ -1298,6 +1310,7 @@
      *     default operation, {@link #MODE_WORLD_READABLE}
      *     and {@link #MODE_WORLD_WRITEABLE} to control permissions.
      *     Use {@link #MODE_ENABLE_WRITE_AHEAD_LOGGING} to enable write-ahead logging by default.
+     *     Use {@link #MODE_NO_LOCALIZED_COLLATORS} to disable localized collators.
      * @param factory An optional factory class that is called to instantiate a
      *     cursor when query is called.
      * @param errorHandler the {@link DatabaseErrorHandler} to be used when sqlite reports database
@@ -1309,6 +1322,7 @@
      * @see #MODE_WORLD_READABLE
      * @see #MODE_WORLD_WRITEABLE
      * @see #MODE_ENABLE_WRITE_AHEAD_LOGGING
+     * @see #MODE_NO_LOCALIZED_COLLATORS
      * @see #deleteDatabase
      */
     public abstract SQLiteDatabase openOrCreateDatabase(String name,
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index e25f1d7..2178c38 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1609,6 +1609,53 @@
             "android.intent.action.MANAGE_PERMISSIONS";
 
     /**
+     * Activity action: Launch UI to review permissions for an app.
+     * The system uses this intent if permission review for apps not
+     * supporting the new runtime permissions model is enabled. In
+     * this mode a permission review is required before any of the
+     * app components can run.
+     * <p>
+     * Input: {@link #EXTRA_PACKAGE_NAME} specifies the package whose
+     * permissions will be reviewed (mandatory).
+     * </p>
+     * <p>
+     * Input: {@link #EXTRA_INTENT} specifies a pending intent to
+     * be fired after the permission review (optional).
+     * </p>
+     * <p>
+     * Input: {@link #EXTRA_REMOTE_CALLBACK} specifies a callback to
+     * be invoked after the permission review (optional).
+     * </p>
+     * <p>
+     * Input: {@link #EXTRA_RESULT_NEEDED} specifies whether the intent
+     * passed via {@link #EXTRA_INTENT} needs a result (optional).
+     * </p>
+     * <p>
+     * Output: Nothing.
+     * </p>
+     *
+     * @see #EXTRA_PACKAGE_NAME
+     * @see #EXTRA_INTENT
+     * @see #EXTRA_REMOTE_CALLBACK
+     * @see #EXTRA_RESULT_NEEDED
+     *
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_REVIEW_PERMISSIONS =
+            "android.intent.action.REVIEW_PERMISSIONS";
+
+    /**
+     * Intent extra: A callback for reporting remote result as a bundle.
+     * <p>
+     * Type: IRemoteCallback
+     * </p>
+     *
+     * @hide
+     */
+    public static final String EXTRA_REMOTE_CALLBACK = "android.intent.extra.REMOTE_CALLBACK";
+
+    /**
      * Intent extra: An app package name.
      * <p>
      * Type: String
@@ -1620,6 +1667,16 @@
     public static final String EXTRA_PACKAGE_NAME = "android.intent.extra.PACKAGE_NAME";
 
     /**
+     * Intent extra: An extra for specifying whether a result is needed.
+     * <p>
+     * Type: boolean
+     * </p>
+     *
+     * @hide
+     */
+    public static final String EXTRA_RESULT_NEEDED = "android.intent.extra.RESULT_NEEDED";
+
+    /**
      * Broadcast action that requests current permission granted information.  It will respond
      * to the request by sending a broadcast with action defined by
      * {@link #EXTRA_GET_PERMISSIONS_RESPONSE_INTENT}. The response will contain
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 0c28008..aa960a4 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2018,7 +2018,6 @@
      */
     public static final int FLAG_PERMISSION_SYSTEM_FIXED =  1 << 4;
 
-
     /**
      * Permission flag: The permission is granted by default because it
      * enables app functionality that is expected to work out-of-the-box
@@ -2030,6 +2029,14 @@
     public static final int FLAG_PERMISSION_GRANTED_BY_DEFAULT =  1 << 5;
 
     /**
+     * Permission flag: The permission has to be reviewed before any of
+     * the app components can run.
+     *
+     * @hide
+     */
+    public static final int FLAG_PERMISSION_REVIEW_REQUIRED =  1 << 6;
+
+    /**
      * Mask for all permission flags.
      *
      * @hide
@@ -4808,6 +4815,7 @@
             case FLAG_PERMISSION_USER_SET: return "USER_SET";
             case FLAG_PERMISSION_REVOKE_ON_UPGRADE: return "REVOKE_ON_UPGRADE";
             case FLAG_PERMISSION_USER_FIXED: return "USER_FIXED";
+            case FLAG_PERMISSION_REVIEW_REQUIRED: return "REVIEW_REQUIRED";
             default: return Integer.toString(flag);
         }
     }
diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java
index 905ac5e..8bf20bf 100644
--- a/core/java/android/content/pm/PackageManagerInternal.java
+++ b/core/java/android/content/pm/PackageManagerInternal.java
@@ -122,4 +122,13 @@
      * @param packageList List of package names to keep cached.
      */
     public abstract void setKeepUninstalledPackages(List<String> packageList);
+
+    /**
+     * Gets whether some of the permissions used by this package require a user
+     * review before any of the app components can run.
+     * @param packageName The package name for which to check.
+     * @param userId The user under which to check.
+     * @return True a permissions review is required.
+     */
+    public abstract boolean isPermissionsReviewRequired(String packageName, int userId);
 }
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index ecb1850..fd1e57b 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -1068,19 +1068,20 @@
         pkg.mSignatures = null;
         pkg.mSigningKeys = null;
 
-        collectCertificates(pkg, new File(pkg.baseCodePath), parseFlags);
+        collectCertificates(pkg, new File(pkg.baseCodePath), pkg.applicationInfo.flags, parseFlags);
 
         if (!ArrayUtils.isEmpty(pkg.splitCodePaths)) {
-            for (String splitCodePath : pkg.splitCodePaths) {
-                collectCertificates(pkg, new File(splitCodePath), parseFlags);
+            for (int i = 0; i < pkg.splitCodePaths.length; i++) {
+                collectCertificates(pkg, new File(pkg.splitCodePaths[i]), pkg.splitFlags[i],
+                        parseFlags);
             }
         }
     }
 
-    private static void collectCertificates(Package pkg, File apkFile, int parseFlags)
+    private static void collectCertificates(Package pkg, File apkFile, int apkFlags, int parseFlags)
             throws PackageParserException {
-        final boolean requireCode = ((parseFlags & PARSE_ENFORCE_CODE) != 0)
-                && ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_HAS_CODE) != 0);
+        final boolean hasCode = (apkFlags & ApplicationInfo.FLAG_HAS_CODE) != 0;
+        final boolean requireCode = ((parseFlags & PARSE_ENFORCE_CODE) != 0) && hasCode;
         final String apkPath = apkFile.getAbsolutePath();
         final boolean skipVerification = Build.IS_DEBUGGABLE
                 && ((parseFlags & PARSE_SKIP_VERIFICATION) != 0);
@@ -1217,7 +1218,8 @@
                 // TODO: factor signature related items out of Package object
                 final Package tempPkg = new Package(null);
                 // TODO: fix b/25118622; pass in '0' for parse flags
-                collectCertificates(tempPkg, apkFile, flags & PARSE_SKIP_VERIFICATION);
+                collectCertificates(tempPkg, apkFile, 0 /*apkFlags*/,
+                        flags & PARSE_SKIP_VERIFICATION);
                 signatures = tempPkg.mSignatures;
             } else {
                 signatures = null;
diff --git a/core/java/android/hardware/input/InputManagerInternal.java b/core/java/android/hardware/input/InputManagerInternal.java
index 6a392dd..014e73f 100644
--- a/core/java/android/hardware/input/InputManagerInternal.java
+++ b/core/java/android/hardware/input/InputManagerInternal.java
@@ -16,8 +16,11 @@
 
 package android.hardware.input;
 
+import android.annotation.Nullable;
 import android.hardware.display.DisplayViewport;
 import android.view.InputEvent;
+import android.view.inputmethod.InputMethodInfo;
+import android.view.inputmethod.InputMethodSubtype;
 
 /**
  * Input manager local system service interface.
@@ -39,4 +42,14 @@
      * watching for wake events.
      */
     public abstract void setInteractive(boolean interactive);
+
+    /**
+     * Notifies that InputMethodManagerService switched the current input method subtype.
+     *
+     * @param userId user id that indicates who is using the specified input method and subtype.
+     * @param inputMethodInfo {@code null} when no input method is selected.
+     * @param subtype {@code null} when {@code inputMethodInfo} does has no subtype.
+     */
+    public abstract void onInputMethodSubtypeChanged(int userId,
+            @Nullable InputMethodInfo inputMethodInfo, @Nullable InputMethodSubtype subtype);
 }
diff --git a/core/java/android/os/AsyncTask.java b/core/java/android/os/AsyncTask.java
index 521df28..63f39c5 100644
--- a/core/java/android/os/AsyncTask.java
+++ b/core/java/android/os/AsyncTask.java
@@ -181,9 +181,12 @@
     private static final String LOG_TAG = "AsyncTask";
 
     private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
-    private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
+    // We want at least 2 threads and at most 4 threads in the core pool,
+    // preferring to have 1 less than the CPU count to avoid saturating
+    // the CPU with background work
+    private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
     private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
-    private static final int KEEP_ALIVE = 1;
+    private static final int KEEP_ALIVE_SECONDS = 30;
 
     private static final ThreadFactory sThreadFactory = new ThreadFactory() {
         private final AtomicInteger mCount = new AtomicInteger(1);
@@ -199,9 +202,15 @@
     /**
      * An {@link Executor} that can be used to execute tasks in parallel.
      */
-    public static final Executor THREAD_POOL_EXECUTOR
-            = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
-                    TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
+    public static final Executor THREAD_POOL_EXECUTOR;
+
+    static {
+        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
+                CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
+                sPoolWorkQueue, sThreadFactory);
+        threadPoolExecutor.allowCoreThreadTimeOut(true);
+        THREAD_POOL_EXECUTOR = threadPoolExecutor;
+    }
 
     /**
      * An {@link Executor} that executes tasks one at a time in serial
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 1aa5c66..bce38f4 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -169,7 +169,7 @@
     /**
      * Current version of checkin data format.
      */
-    static final String CHECKIN_VERSION = "16";
+    static final String CHECKIN_VERSION = "17";
 
     /**
      * Old version, we hit 9 and ran out of room, need to remove.
@@ -407,17 +407,23 @@
         public abstract Timer getCameraTurnedOnTimer();
         public abstract Timer getForegroundActivityTimer();
 
-        // Time this uid has any processes in foreground state.
-        public static final int PROCESS_STATE_FOREGROUND = 0;
-        // Time this uid has any process in active state (not cached).
-        public static final int PROCESS_STATE_ACTIVE = 1;
+        // Time this uid has any processes in the top state.
+        public static final int PROCESS_STATE_TOP = 0;
+        // Time this uid has any process with a started out bound foreground service.
+        public static final int PROCESS_STATE_FOREGROUND_SERVICE = 1;
+        // Time this uid has any process that is top while the device is sleeping.
+        public static final int PROCESS_STATE_TOP_SLEEPING = 2;
+        // Time this uid has any process in an active foreground state.
+        public static final int PROCESS_STATE_FOREGROUND = 3;
+        // Time this uid has any process in an active background state.
+        public static final int PROCESS_STATE_BACKGROUND = 4;
         // Time this uid has any processes running at all.
-        public static final int PROCESS_STATE_RUNNING = 2;
+        public static final int PROCESS_STATE_CACHED = 5;
         // Total number of process states we track.
-        public static final int NUM_PROCESS_STATE = 3;
+        public static final int NUM_PROCESS_STATE = 6;
 
         static final String[] PROCESS_STATE_NAMES = {
-            "Foreground", "Active", "Running"
+            "Top", "Fg Service", "Top Sleeping", "Foreground", "Background", "Cached"
         };
 
         public abstract long getProcessStateTime(int state, long elapsedRealtimeUs, int which);
@@ -2954,8 +2960,9 @@
             final Object[] stateTimes = new Object[Uid.NUM_PROCESS_STATE];
             long totalStateTime = 0;
             for (int ips=0; ips<Uid.NUM_PROCESS_STATE; ips++) {
-                totalStateTime += u.getProcessStateTime(ips, rawRealtime, which);
-                stateTimes[ips] = (totalStateTime + 500) / 1000;
+                final long time = u.getProcessStateTime(ips, rawRealtime, which);
+                totalStateTime += time;
+                stateTimes[ips] = (time + 500) / 1000;
             }
             if (totalStateTime > 0) {
                 dumpLine(pw, uid, category, STATE_TIME_DATA, stateTimes);
@@ -4122,11 +4129,18 @@
                     sb.append("    ");
                     sb.append(Uid.PROCESS_STATE_NAMES[ips]);
                     sb.append(" for: ");
-                    formatTimeMs(sb, (totalStateTime + 500) / 1000);
+                    formatTimeMs(sb, (time + 500) / 1000);
                     pw.println(sb.toString());
                     uidActivity = true;
                 }
             }
+            if (totalStateTime > 0) {
+                sb.setLength(0);
+                sb.append(prefix);
+                sb.append("    Total running: ");
+                formatTimeMs(sb, (totalStateTime + 500) / 1000);
+                pw.println(sb.toString());
+            }
 
             final long userCpuTimeUs = u.getUserCpuTimeUs(which);
             final long systemCpuTimeUs = u.getSystemCpuTimeUs(which);
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index f7c8662..de8b690 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -788,6 +788,18 @@
             SystemProperties.getInt("ro.debuggable", 0) == 1;
 
     /**
+     * Specifies whether the permissions needed by a legacy app should be
+     * reviewed before any of its components can run. A legacy app is one
+     * with targetSdkVersion < 23, i.e apps using the old permission model.
+     * If review is not required, permissions are reviewed before the app
+     * is installed.
+     *
+     * @hide
+     */
+    public static final boolean PERMISSIONS_REVIEW_REQUIRED =
+            SystemProperties.getInt("ro.permission_review_required", 0) == 1;
+
+    /**
      * Returns the version string for the radio firmware.  May return
      * null (if, for instance, the radio is not currently on).
      */
diff --git a/core/java/android/os/IProcessInfoService.aidl b/core/java/android/os/IProcessInfoService.aidl
index c98daa2..62237f5 100644
--- a/core/java/android/os/IProcessInfoService.aidl
+++ b/core/java/android/os/IProcessInfoService.aidl
@@ -25,5 +25,12 @@
      * to indicate that no process with the given PID exists.
      */
     void getProcessStatesFromPids(in int[] pids, out int[] states);
+
+    /**
+     * For each PID in the given input array, write the current process state and OOM score
+     * for that process into the output arrays, or ActivityManager.PROCESS_STATE_NONEXISTENT
+     * in the states array to indicate that no process with the given PID exists.
+     */
+    void getProcessStatesAndOomScoresFromPids(in int[] pids, out int[] states, out int[] scores);
 }
 
diff --git a/core/java/android/os/RemoteCallback.java b/core/java/android/os/RemoteCallback.java
index ca95bdf..89e30a9 100644
--- a/core/java/android/os/RemoteCallback.java
+++ b/core/java/android/os/RemoteCallback.java
@@ -16,88 +16,84 @@
 
 package android.os;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
 /**
- * TODO: Make this a public API?  Let's see how it goes with a few use
- * cases first.
  * @hide
  */
-public abstract class RemoteCallback implements Parcelable {
-    final Handler mHandler;
-    final IRemoteCallback mTarget;
-    
-    class DeliverResult implements Runnable {
-        final Bundle mResult;
-        
-        DeliverResult(Bundle result) {
-            mResult = result;
-        }
-        
-        public void run() {
-            onResult(mResult);
-        }
+public final class RemoteCallback implements Parcelable {
+
+    public interface OnResultListener {
+        public void onResult(Bundle result);
     }
-    
-    class LocalCallback extends IRemoteCallback.Stub {
-        public void sendResult(Bundle bundle) {
-            mHandler.post(new DeliverResult(bundle));
-        }
+
+    private final OnResultListener mListener;
+    private final Handler mHandler;
+    private final IRemoteCallback mCallback;
+
+    public RemoteCallback(OnResultListener listener) {
+        this(listener, null);
     }
-    
-    static class RemoteCallbackProxy extends RemoteCallback {
-        RemoteCallbackProxy(IRemoteCallback target) {
-            super(target);
+
+    public RemoteCallback(@NonNull OnResultListener listener, @Nullable Handler handler) {
+        if (listener == null) {
+            throw new NullPointerException("listener cannot be null");
         }
-        
-        protected void onResult(Bundle bundle) {
-        }
-    }
-    
-    public RemoteCallback(Handler handler) {
+        mListener = listener;
         mHandler = handler;
-        mTarget = new LocalCallback();
+        mCallback = new IRemoteCallback.Stub() {
+            @Override
+            public void sendResult(Bundle data) {
+                RemoteCallback.this.sendResult(data);
+            }
+        };
     }
-    
-     RemoteCallback(IRemoteCallback target) {
+
+    RemoteCallback(Parcel parcel) {
+        mListener = null;
         mHandler = null;
-        mTarget = target;
+        mCallback = IRemoteCallback.Stub.asInterface(
+                parcel.readStrongBinder());
     }
-    
-    public void sendResult(Bundle bundle) throws RemoteException {
-        mTarget.sendResult(bundle);
-    }
-    
-    protected abstract void onResult(Bundle bundle);
-    
-    public boolean equals(Object otherObj) {
-        if (otherObj == null) {
-            return false;
+
+    public void sendResult(@Nullable final Bundle result) {
+        // Do local dispatch
+        if (mListener != null) {
+            if (mHandler != null) {
+                mHandler.post(new Runnable() {
+                    @Override
+                    public void run() {
+                        mListener.onResult(result);
+                    }
+                });
+            } else {
+                mListener.onResult(result);
+            }
+        // Do remote dispatch
+        } else {
+            try {
+                mCallback.sendResult(result);
+            } catch (RemoteException e) {
+                /* ignore */
+            }
         }
-        try {
-            return mTarget.asBinder().equals(((RemoteCallback)otherObj)
-                    .mTarget.asBinder());
-        } catch (ClassCastException e) {
-        }
-        return false;
     }
-    
-    public int hashCode() {
-        return mTarget.asBinder().hashCode();
-    }
-    
+
+    @Override
     public int describeContents() {
         return 0;
     }
 
-    public void writeToParcel(Parcel out, int flags) {
-        out.writeStrongBinder(mTarget.asBinder());
+    @Override
+    public void writeToParcel(Parcel parcel, int flags) {
+        parcel.writeStrongBinder(mCallback.asBinder());
     }
 
     public static final Parcelable.Creator<RemoteCallback> CREATOR
             = new Parcelable.Creator<RemoteCallback>() {
-        public RemoteCallback createFromParcel(Parcel in) {
-            IBinder target = in.readStrongBinder();
-            return target != null ? new RemoteCallbackProxy(
-                    IRemoteCallback.Stub.asInterface(target)) : null;
+        public RemoteCallback createFromParcel(Parcel parcel) {
+            return new RemoteCallback(parcel);
         }
 
         public RemoteCallback[] newArray(int size) {
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 0a149bb..79390d4 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -487,6 +487,19 @@
     public static final String DISALLOW_RECORD_AUDIO = "no_record_audio";
 
     /**
+     * Specifies if a user is not allowed to run in the background and should be stopped during
+     * user switch. The default value is <code>false</code>.
+     *
+     * <p>This restriction can be set by device owners and profile owners.
+     *
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     * @hide
+     */
+    public static final String DISALLOW_RUN_IN_BACKGROUND = "no_run_in_background";
+
+    /**
      * Specifies if a user is not allowed to use the camera.
      *
      * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
@@ -742,6 +755,23 @@
     }
 
     /**
+     * Return whether the given user is running in an "unlocked" state. A user
+     * is unlocked only after they've entered their credentials (such as a lock
+     * pattern or PIN), and credential-encrypted private app data storage is
+     * available.
+     *
+     * @param user to retrieve the unlocked state for.
+     */
+    public boolean isUserRunningUnlocked(UserHandle user) {
+        try {
+            return ActivityManagerNative.getDefault().isUserRunning(
+                    user.getIdentifier(), ActivityManager.FLAG_AND_UNLOCKED);
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
+    /**
      * Returns the UserInfo object describing a specific user.
      * Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
      * @param userHandle the user handle of the user whose information is being requested.
@@ -929,9 +959,6 @@
             if (guest != null) {
                 Settings.Secure.putStringForUser(context.getContentResolver(),
                         Settings.Secure.SKIP_FIRST_USE_HINTS, "1", guest.id);
-
-                mService.setUserRestriction(DISALLOW_SMS, true, guest.id);
-                mService.setUserRestriction(DISALLOW_INSTALL_UNKNOWN_SOURCES, true, guest.id);
             }
         } catch (RemoteException re) {
             Log.w(TAG, "Could not create a user", re);
diff --git a/core/java/android/print/IPrintSpooler.aidl b/core/java/android/print/IPrintSpooler.aidl
index db2bf1a..b7cfbea 100644
--- a/core/java/android/print/IPrintSpooler.aidl
+++ b/core/java/android/print/IPrintSpooler.aidl
@@ -41,6 +41,23 @@
     void createPrintJob(in PrintJobInfo printJob);
     void setPrintJobState(in PrintJobId printJobId, int status, String stateReason,
             IPrintSpoolerCallbacks callback, int sequence);
+
+    /**
+     * Set the progress of this print job
+     *
+     * @param printJobId The print job to update
+     * @param progress The new progress
+     */
+    void setProgress(in PrintJobId printJobId, in float progress);
+
+    /**
+     * Set the status of this print job
+     *
+     * @param printJobId The print job to update
+     * @param status The new status, can be null
+     */
+    void setStatus(in PrintJobId printJobId, in CharSequence status);
+
     void setPrintJobTag(in PrintJobId printJobId, String tag, IPrintSpoolerCallbacks callback,
             int sequence);
     void writePrintJobData(in ParcelFileDescriptor fd, in PrintJobId printJobId);
diff --git a/core/java/android/print/PrintJobInfo.java b/core/java/android/print/PrintJobInfo.java
index 63f94fe..7148c87 100644
--- a/core/java/android/print/PrintJobInfo.java
+++ b/core/java/android/print/PrintJobInfo.java
@@ -16,10 +16,15 @@
 
 package android.print;
 
+import android.annotation.FloatRange;
+import android.annotation.Nullable;
+import android.annotation.TestApi;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import com.android.internal.util.Preconditions;
+
 import java.util.Arrays;
 
 /**
@@ -149,9 +154,6 @@
     /** How many copies to print. */
     private int mCopies;
 
-    /** Reason for the print job being in its current state. */
-    private String mStateReason;
-
     /** The pages to print */
     private PageRange[] mPageRanges;
 
@@ -161,6 +163,12 @@
     /** Information about the printed document. */
     private PrintDocumentInfo mDocumentInfo;
 
+    /** The progress made on printing this job or -1 if not set. */
+    private float mProgress;
+
+    /** A short string describing the status of this job. */
+    private CharSequence mStatus;
+
     /** Advanced printer specific options. */
     private Bundle mAdvancedOptions;
 
@@ -169,7 +177,7 @@
 
     /** @hide*/
     public PrintJobInfo() {
-        /* do nothing */
+        mProgress = -1;
     }
 
     /** @hide */
@@ -183,10 +191,11 @@
         mTag = other.mTag;
         mCreationTime = other.mCreationTime;
         mCopies = other.mCopies;
-        mStateReason = other.mStateReason;
         mPageRanges = other.mPageRanges;
         mAttributes = other.mAttributes;
         mDocumentInfo = other.mDocumentInfo;
+        mProgress = other.mProgress;
+        mStatus = other.mStatus;
         mCanceling = other.mCanceling;
         mAdvancedOptions = other.mAdvancedOptions;
     }
@@ -201,7 +210,6 @@
         mTag = parcel.readString();
         mCreationTime = parcel.readLong();
         mCopies = parcel.readInt();
-        mStateReason = parcel.readString();
         Parcelable[] parcelables = parcel.readParcelableArray(null);
         if (parcelables != null) {
             mPageRanges = new PageRange[parcelables.length];
@@ -211,6 +219,8 @@
         }
         mAttributes = (PrintAttributes) parcel.readParcelable(null);
         mDocumentInfo = (PrintDocumentInfo) parcel.readParcelable(null);
+        mProgress = parcel.readFloat();
+        mStatus = parcel.readCharSequence();
         mCanceling = (parcel.readInt() == 1);
         mAdvancedOptions = parcel.readBundle();
     }
@@ -227,7 +237,7 @@
     /**
      * Sets the unique print job id.
      *
-     * @param The job id.
+     * @param id The job id.
      *
      * @hide
      */
@@ -265,7 +275,7 @@
     }
 
     /**
-     * Sets the unique target pritner id.
+     * Sets the unique target printer id.
      *
      * @param printerId The target printer id.
      *
@@ -326,6 +336,30 @@
     }
 
     /**
+     * Sets the progress of the print job.
+     *
+     * @param progress the progress of the job
+     *
+     * @hide
+     */
+    public void setProgress(@FloatRange(from=0.0, to=1.0) float progress) {
+        Preconditions.checkArgumentInRange(progress, 0, 1, "progress");
+
+        mProgress = progress;
+    }
+
+    /**
+     * Sets the status of the print job.
+     *
+     * @param status the status of the job, can be null
+     *
+     * @hide
+     */
+    public void setStatus(@Nullable CharSequence status) {
+        mStatus = status;
+    }
+
+    /**
      * Sets the owning application id.
      *
      * @return The owning app id.
@@ -416,30 +450,6 @@
     }
 
     /**
-     * Gets the reason for the print job being in the current state.
-     *
-     * @return The reason, or null if there is no reason or the
-     * reason is unknown.
-     *
-     * @hide
-     */
-    public String getStateReason() {
-        return mStateReason;
-    }
-
-    /**
-     * Sets the reason for the print job being in the current state.
-     *
-     * @param stateReason The reason, or null if there is no reason
-     * or the reason is unknown.
-     *
-     * @hide
-     */
-    public void setStateReason(String stateReason) {
-        mStateReason = stateReason;
-    }
-
-    /**
      * Gets the included pages.
      *
      * @return The included pages or <code>null</code> if not set.
@@ -604,10 +614,11 @@
         parcel.writeString(mTag);
         parcel.writeLong(mCreationTime);
         parcel.writeInt(mCopies);
-        parcel.writeString(mStateReason);
         parcel.writeParcelableArray(mPageRanges, flags);
         parcel.writeParcelable(mAttributes, flags);
         parcel.writeParcelable(mDocumentInfo, 0);
+        parcel.writeFloat(mProgress);
+        parcel.writeCharSequence(mStatus);
         parcel.writeInt(mCanceling ? 1 : 0);
         parcel.writeBundle(mAdvancedOptions);
     }
@@ -631,6 +642,9 @@
         builder.append(", pages: " + (mPageRanges != null
                 ? Arrays.toString(mPageRanges) : null));
         builder.append(", hasAdvancedOptions: " + (mAdvancedOptions != null));
+        builder.append(", progress: " + mProgress);
+        builder.append(", status: " + (mStatus != null
+                ? mStatus.toString() : null));
         builder.append("}");
         return builder.toString();
     }
@@ -666,6 +680,28 @@
     }
 
     /**
+     * Get the progress that has been made printing this job.
+     *
+     * @return the print progress or -1 if not set
+     * @hide
+     */
+    @TestApi
+    public float getProgress() {
+        return mProgress;
+    }
+
+    /**
+     * Get the status of this job.
+     *
+     * @return the status of this job or null if not set
+     * @hide
+     */
+    @TestApi
+    public @Nullable CharSequence getStatus() {
+        return mStatus;
+    }
+
+    /**
      * Builder for creating a {@link PrintJobInfo}.
      */
     public static final class Builder {
@@ -711,6 +747,28 @@
         }
 
         /**
+         * Sets the progress of the print job.
+         *
+         * @param progress the progress of the job
+         * @hide
+         */
+        public void setProgress(@FloatRange(from=0.0, to=1.0) float progress) {
+            Preconditions.checkArgumentInRange(progress, 0, 1, "progress");
+
+            mPrototype.mProgress = progress;
+        }
+
+        /**
+         * Sets the status of the print job.
+         *
+         * @param status the status of the job, can be null
+         * @hide
+         */
+        public void setStatus(@Nullable CharSequence status) {
+            mPrototype.mStatus = status;
+        }
+
+        /**
          * Puts an advanced (printer specific) option.
          *
          * @param key The option key.
diff --git a/core/java/android/printservice/IPrintServiceClient.aidl b/core/java/android/printservice/IPrintServiceClient.aidl
index c2dfc30..b4baa48 100644
--- a/core/java/android/printservice/IPrintServiceClient.aidl
+++ b/core/java/android/printservice/IPrintServiceClient.aidl
@@ -35,6 +35,22 @@
     boolean setPrintJobTag(in PrintJobId printJobId, String tag);
     oneway void writePrintJobData(in ParcelFileDescriptor fd, in PrintJobId printJobId);
 
+    /**
+     * Set the progress of this print job
+     *
+     * @param printJobId The print job to update
+     * @param progress The new progress
+     */
+    void setProgress(in PrintJobId printJobId, in float progress);
+
+    /**
+     * Set the status of this print job
+     *
+     * @param printJobId The print job to update
+     * @param status The new status, can be null
+     */
+    void setStatus(in PrintJobId printJobId, in CharSequence status);
+
     void onPrintersAdded(in ParceledListSlice printers);
     void onPrintersRemoved(in ParceledListSlice printerIds);
 }
diff --git a/core/java/android/printservice/PrintJob.java b/core/java/android/printservice/PrintJob.java
index 6fa0bdd..86fc292 100644
--- a/core/java/android/printservice/PrintJob.java
+++ b/core/java/android/printservice/PrintJob.java
@@ -16,6 +16,10 @@
 
 package android.printservice;
 
+import android.annotation.FloatRange;
+import android.annotation.MainThread;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.os.RemoteException;
 import android.print.PrintJobId;
 import android.print.PrintJobInfo;
@@ -41,7 +45,7 @@
 
     private PrintJobInfo mCachedInfo;
 
-    PrintJob(PrintJobInfo jobInfo, IPrintServiceClient client) {
+    PrintJob(@NonNull PrintJobInfo jobInfo, @NonNull IPrintServiceClient client) {
         mCachedInfo = jobInfo;
         mPrintServiceClient = client;
         mDocument = new PrintDocument(mCachedInfo.getId(), client,
@@ -53,6 +57,7 @@
      *
      * @return The id.
      */
+    @MainThread
     public PrintJobId getId() {
         PrintService.throwIfNotCalledOnMainThread();
         return mCachedInfo.getId();
@@ -68,7 +73,8 @@
      *
      * @return The print job info.
      */
-    public PrintJobInfo getInfo() {
+    @MainThread
+    public @NonNull PrintJobInfo getInfo() {
         PrintService.throwIfNotCalledOnMainThread();
         if (isInImmutableState()) {
             return mCachedInfo;
@@ -90,7 +96,8 @@
      *
      * @return The document.
      */
-    public PrintDocument getDocument() {
+    @MainThread
+    public @NonNull PrintDocument getDocument() {
         PrintService.throwIfNotCalledOnMainThread();
         return mDocument;
     }
@@ -104,6 +111,7 @@
      * @see #start()
      * @see #cancel()
      */
+    @MainThread
     public boolean isQueued() {
         PrintService.throwIfNotCalledOnMainThread();
         return getInfo().getState() == PrintJobInfo.STATE_QUEUED;
@@ -117,8 +125,9 @@
      *
      * @see #complete()
      * @see #cancel()
-     * @see #fail(CharSequence)
+     * @see #fail(String)
      */
+    @MainThread
     public boolean isStarted() {
         PrintService.throwIfNotCalledOnMainThread();
         return getInfo().getState() == PrintJobInfo.STATE_STARTED;
@@ -132,8 +141,9 @@
      *
      * @see #start()
      * @see #cancel()
-     * @see #fail(CharSequence)
+     * @see #fail(String)
      */
+    @MainThread
     public boolean isBlocked() {
         PrintService.throwIfNotCalledOnMainThread();
         return getInfo().getState() == PrintJobInfo.STATE_BLOCKED;
@@ -147,6 +157,7 @@
      *
      * @see #complete()
      */
+    @MainThread
     public boolean isCompleted() {
         PrintService.throwIfNotCalledOnMainThread();
         return getInfo().getState() == PrintJobInfo.STATE_COMPLETED;
@@ -158,8 +169,9 @@
      *
      * @return Whether the print job is failed.
      *
-     * @see #fail(CharSequence)
+     * @see #fail(String)
      */
+    @MainThread
     public boolean isFailed() {
         PrintService.throwIfNotCalledOnMainThread();
         return getInfo().getState() == PrintJobInfo.STATE_FAILED;
@@ -173,6 +185,7 @@
      *
      * @see #cancel()
      */
+    @MainThread
     public boolean isCancelled() {
         PrintService.throwIfNotCalledOnMainThread();
         return getInfo().getState() == PrintJobInfo.STATE_CANCELED;
@@ -182,12 +195,16 @@
      * Starts the print job. You should call this method if {@link
      * #isQueued()} or {@link #isBlocked()} returns true and you started
      * resumed printing.
+     * <p>
+     * This resets the print status to null. Set the new status by using {@link #setStatus}.
+     * </p>
      *
      * @return Whether the job was started.
      *
      * @see #isQueued()
      * @see #isBlocked()
      */
+    @MainThread
     public boolean start() {
         PrintService.throwIfNotCalledOnMainThread();
         final int state = getInfo().getState();
@@ -205,18 +222,20 @@
      * paper to continue printing. To resume the print job call {@link
      * #start()}.
      *
+     * @param reason The human readable, short, and translated reason why the print job is blocked.
      * @return Whether the job was blocked.
      *
      * @see #isStarted()
      * @see #isBlocked()
      */
-    public boolean block(String reason) {
+    @MainThread
+    public boolean block(@Nullable String reason) {
         PrintService.throwIfNotCalledOnMainThread();
         PrintJobInfo info = getInfo();
         final int state = info.getState();
         if (state == PrintJobInfo.STATE_STARTED
                 || (state == PrintJobInfo.STATE_BLOCKED
-                        && !TextUtils.equals(info.getStateReason(), reason))) {
+                        && !TextUtils.equals(info.getStatus(), reason))) {
             return setState(PrintJobInfo.STATE_BLOCKED, reason);
         }
         return false;
@@ -230,6 +249,7 @@
      *
      * @see #isStarted()
      */
+    @MainThread
     public boolean complete() {
         PrintService.throwIfNotCalledOnMainThread();
         if (isStarted()) {
@@ -251,7 +271,8 @@
      * @see #isStarted()
      * @see #isBlocked()
      */
-    public boolean fail(String error) {
+    @MainThread
+    public boolean fail(@Nullable String error) {
         PrintService.throwIfNotCalledOnMainThread();
         if (!isInImmutableState()) {
             return setState(PrintJobInfo.STATE_FAILED, error);
@@ -271,6 +292,7 @@
      * @see #isQueued()
      * @see #isBlocked()
      */
+    @MainThread
     public boolean cancel() {
         PrintService.throwIfNotCalledOnMainThread();
         if (!isInImmutableState()) {
@@ -280,6 +302,39 @@
     }
 
     /**
+     * Sets the progress of this print job as a fraction of 1.
+     *
+     * @param progress The new progress
+     */
+    @MainThread
+    public void setProgress(@FloatRange(from=0.0, to=1.0) float progress) {
+        PrintService.throwIfNotCalledOnMainThread();
+
+        try {
+            mPrintServiceClient.setProgress(mCachedInfo.getId(), progress);
+        } catch (RemoteException re) {
+            Log.e(LOG_TAG, "Error setting progress for job: " + mCachedInfo.getId(), re);
+        }
+    }
+
+    /**
+     * Sets the status of this print job. This should be a human readable, short, and translated
+     * description of the current state of the print job.
+     *
+     * @param status The new status. If null the status will be empty.
+     */
+    @MainThread
+    public void setStatus(@Nullable CharSequence status) {
+        PrintService.throwIfNotCalledOnMainThread();
+
+        try {
+            mPrintServiceClient.setStatus(mCachedInfo.getId(), status);
+        } catch (RemoteException re) {
+            Log.e(LOG_TAG, "Error setting status for job: " + mCachedInfo.getId(), re);
+        }
+    }
+
+    /**
      * Sets a tag that is valid in the context of a {@link PrintService}
      * and is not interpreted by the system. For example, a print service
      * may set as a tag the key of the print job returned by a remote
@@ -288,6 +343,7 @@
      * @param tag The tag.
      * @return True if the tag was set, false otherwise.
      */
+    @MainThread
     public boolean setTag(String tag) {
         PrintService.throwIfNotCalledOnMainThread();
         if (isInImmutableState()) {
@@ -319,6 +375,7 @@
      * @param key The option key.
      * @return The option value.
      */
+    @MainThread
     public String getAdvancedStringOption(String key) {
         PrintService.throwIfNotCalledOnMainThread();
         return getInfo().getAdvancedStringOption(key);
@@ -331,6 +388,7 @@
      * @param key The option key.
      * @return Whether the option is present.
      */
+    @MainThread
     public boolean hasAdvancedOption(String key) {
         PrintService.throwIfNotCalledOnMainThread();
         return getInfo().hasAdvancedOption(key);
@@ -342,6 +400,7 @@
      * @param key The option key.
      * @return The option value.
      */
+    @MainThread
     public int getAdvancedIntOption(String key) {
         PrintService.throwIfNotCalledOnMainThread();
         return getInfo().getAdvancedIntOption(key);
@@ -374,14 +433,14 @@
                 || state == PrintJobInfo.STATE_FAILED;
     }
 
-    private boolean setState(int state, String error) {
+    private boolean setState(int state, @Nullable String error) {
         try {
             if (mPrintServiceClient.setPrintJobState(mCachedInfo.getId(), state, error)) {
                 // Best effort - update the state of the cached info since
                 // we may not be able to re-fetch it later if the job gets
                 // removed from the spooler as a result of the state change.
                 mCachedInfo.setState(state);
-                mCachedInfo.setStateReason(error);
+                mCachedInfo.setStatus(error);
                 return true;
             }
         } catch (RemoteException re) {
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index 8ae899f..d53bb0d 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -67,7 +67,7 @@
  * @see DocumentsProvider
  */
 public final class DocumentsContract {
-    private static final String TAG = "Documents";
+    private static final String TAG = "DocumentsContract";
 
     // content://com.example/root/
     // content://com.example/root/sdcard/
@@ -591,6 +591,12 @@
      */
     public static final String EXTRA_ERROR = "error";
 
+    /**
+     * Optional result (I'm thinking boolean) answer to a question.
+     * {@hide}
+     */
+    public static final String EXTRA_RESULT = "result";
+
     /** {@hide} */
     public static final String METHOD_CREATE_DOCUMENT = "android:createDocument";
     /** {@hide} */
@@ -601,6 +607,8 @@
     public static final String METHOD_COPY_DOCUMENT = "android:copyDocument";
     /** {@hide} */
     public static final String METHOD_MOVE_DOCUMENT = "android:moveDocument";
+    /** {@hide} */
+    public static final String METHOD_IS_CHILD_DOCUMENT = "android:isChildDocument";
 
     /** {@hide} */
     public static final String EXTRA_URI = "uri";
@@ -1025,6 +1033,24 @@
         return out.getParcelable(DocumentsContract.EXTRA_URI);
     }
 
+    /** {@hide} */
+    public static boolean isChildDocument(ContentProviderClient client, Uri parentDocumentUri,
+            Uri childDocumentUri) throws RemoteException {
+
+        final Bundle in = new Bundle();
+        in.putParcelable(DocumentsContract.EXTRA_URI, parentDocumentUri);
+        in.putParcelable(DocumentsContract.EXTRA_TARGET_URI, childDocumentUri);
+
+        final Bundle out = client.call(METHOD_IS_CHILD_DOCUMENT, null, in);
+        if (out == null) {
+            throw new RemoteException("Failed to get a reponse from isChildDocument query.");
+        }
+        if (!out.containsKey(DocumentsContract.EXTRA_RESULT)) {
+            throw new RemoteException("Response did not include result field..");
+        }
+        return out.getBoolean(DocumentsContract.EXTRA_RESULT);
+    }
+
     /**
      * Change the display name of an existing document.
      * <p>
diff --git a/core/java/android/provider/DocumentsProvider.java b/core/java/android/provider/DocumentsProvider.java
index f01073b..e25ba35 100644
--- a/core/java/android/provider/DocumentsProvider.java
+++ b/core/java/android/provider/DocumentsProvider.java
@@ -16,11 +16,12 @@
 
 package android.provider;
 
+import static android.provider.DocumentsContract.METHOD_COPY_DOCUMENT;
 import static android.provider.DocumentsContract.METHOD_CREATE_DOCUMENT;
 import static android.provider.DocumentsContract.METHOD_DELETE_DOCUMENT;
-import static android.provider.DocumentsContract.METHOD_RENAME_DOCUMENT;
-import static android.provider.DocumentsContract.METHOD_COPY_DOCUMENT;
+import static android.provider.DocumentsContract.METHOD_IS_CHILD_DOCUMENT;
 import static android.provider.DocumentsContract.METHOD_MOVE_DOCUMENT;
+import static android.provider.DocumentsContract.METHOD_RENAME_DOCUMENT;
 import static android.provider.DocumentsContract.buildDocumentUri;
 import static android.provider.DocumentsContract.buildDocumentUriMaybeUsingTree;
 import static android.provider.DocumentsContract.buildTreeDocumentUri;
@@ -688,6 +689,16 @@
             return super.call(method, arg, extras);
         }
 
+        try {
+            return callUnchecked(method, arg, extras);
+        } catch (FileNotFoundException e) {
+            throw new IllegalStateException("Failed call " + method, e);
+        }
+    }
+
+    private Bundle callUnchecked(String method, String arg, Bundle extras)
+            throws FileNotFoundException {
+
         final Context context = getContext();
         final Uri documentUri = extras.getParcelable(DocumentsContract.EXTRA_URI);
         final String authority = documentUri.getAuthority();
@@ -697,109 +708,120 @@
             throw new SecurityException(
                     "Requested authority " + authority + " doesn't match provider " + mAuthority);
         }
-        enforceTree(documentUri);
 
         final Bundle out = new Bundle();
-        try {
-            if (METHOD_CREATE_DOCUMENT.equals(method)) {
-                enforceWritePermissionInner(documentUri, getCallingPackage(), null);
 
-                final String mimeType = extras.getString(Document.COLUMN_MIME_TYPE);
-                final String displayName = extras.getString(Document.COLUMN_DISPLAY_NAME);
-                final String newDocumentId = createDocument(documentId, mimeType, displayName);
+        // If the URI is a tree URI performs some validation.
+        enforceTree(documentUri);
 
-                // No need to issue new grants here, since caller either has
-                // manage permission or a prefix grant. We might generate a
-                // tree style URI if that's how they called us.
+        if (METHOD_IS_CHILD_DOCUMENT.equals(method)) {
+            enforceReadPermissionInner(documentUri, getCallingPackage(), null);
+
+            final Uri childUri = extras.getParcelable(DocumentsContract.EXTRA_TARGET_URI);
+            final String childAuthority = childUri.getAuthority();
+            final String childId = DocumentsContract.getDocumentId(childUri);
+
+            out.putBoolean(
+                    DocumentsContract.EXTRA_RESULT,
+                    mAuthority.equals(childAuthority)
+                            && isChildDocument(documentId, childId));
+
+        } else if (METHOD_CREATE_DOCUMENT.equals(method)) {
+            enforceWritePermissionInner(documentUri, getCallingPackage(), null);
+
+            final String mimeType = extras.getString(Document.COLUMN_MIME_TYPE);
+            final String displayName = extras.getString(Document.COLUMN_DISPLAY_NAME);
+            final String newDocumentId = createDocument(documentId, mimeType, displayName);
+
+            // No need to issue new grants here, since caller either has
+            // manage permission or a prefix grant. We might generate a
+            // tree style URI if that's how they called us.
+            final Uri newDocumentUri = buildDocumentUriMaybeUsingTree(documentUri,
+                    newDocumentId);
+            out.putParcelable(DocumentsContract.EXTRA_URI, newDocumentUri);
+
+        } else if (METHOD_RENAME_DOCUMENT.equals(method)) {
+            enforceWritePermissionInner(documentUri, getCallingPackage(), null);
+
+            final String displayName = extras.getString(Document.COLUMN_DISPLAY_NAME);
+            final String newDocumentId = renameDocument(documentId, displayName);
+
+            if (newDocumentId != null) {
                 final Uri newDocumentUri = buildDocumentUriMaybeUsingTree(documentUri,
                         newDocumentId);
+
+                // If caller came in with a narrow grant, issue them a
+                // narrow grant for the newly renamed document.
+                if (!isTreeUri(newDocumentUri)) {
+                    final int modeFlags = getCallingOrSelfUriPermissionModeFlags(context,
+                            documentUri);
+                    context.grantUriPermission(getCallingPackage(), newDocumentUri, modeFlags);
+                }
+
                 out.putParcelable(DocumentsContract.EXTRA_URI, newDocumentUri);
 
-            } else if (METHOD_RENAME_DOCUMENT.equals(method)) {
-                enforceWritePermissionInner(documentUri, getCallingPackage(), null);
-
-                final String displayName = extras.getString(Document.COLUMN_DISPLAY_NAME);
-                final String newDocumentId = renameDocument(documentId, displayName);
-
-                if (newDocumentId != null) {
-                    final Uri newDocumentUri = buildDocumentUriMaybeUsingTree(documentUri,
-                            newDocumentId);
-
-                    // If caller came in with a narrow grant, issue them a
-                    // narrow grant for the newly renamed document.
-                    if (!isTreeUri(newDocumentUri)) {
-                        final int modeFlags = getCallingOrSelfUriPermissionModeFlags(context,
-                                documentUri);
-                        context.grantUriPermission(getCallingPackage(), newDocumentUri, modeFlags);
-                    }
-
-                    out.putParcelable(DocumentsContract.EXTRA_URI, newDocumentUri);
-
-                    // Original document no longer exists, clean up any grants
-                    revokeDocumentPermission(documentId);
-                }
-
-            } else if (METHOD_DELETE_DOCUMENT.equals(method)) {
-                enforceWritePermissionInner(documentUri, getCallingPackage(), null);
-                deleteDocument(documentId);
-
-                // Document no longer exists, clean up any grants
-                revokeDocumentPermission(documentId);
-
-            } else if (METHOD_COPY_DOCUMENT.equals(method)) {
-                final Uri targetUri = extras.getParcelable(DocumentsContract.EXTRA_TARGET_URI);
-                final String targetId = DocumentsContract.getDocumentId(targetUri);
-
-                enforceReadPermissionInner(documentUri, getCallingPackage(), null);
-                enforceWritePermissionInner(targetUri, getCallingPackage(), null);
-
-                final String newDocumentId = copyDocument(documentId, targetId);
-
-                if (newDocumentId != null) {
-                    final Uri newDocumentUri = buildDocumentUriMaybeUsingTree(documentUri,
-                            newDocumentId);
-
-                    if (!isTreeUri(newDocumentUri)) {
-                        final int modeFlags = getCallingOrSelfUriPermissionModeFlags(context,
-                                documentUri);
-                        context.grantUriPermission(getCallingPackage(), newDocumentUri, modeFlags);
-                    }
-
-                    out.putParcelable(DocumentsContract.EXTRA_URI, newDocumentUri);
-                }
-
-            } else if (METHOD_MOVE_DOCUMENT.equals(method)) {
-                final Uri targetUri = extras.getParcelable(DocumentsContract.EXTRA_TARGET_URI);
-                final String targetId = DocumentsContract.getDocumentId(targetUri);
-
-                enforceReadPermissionInner(documentUri, getCallingPackage(), null);
-                enforceWritePermissionInner(targetUri, getCallingPackage(), null);
-
-                final String displayName = extras.getString(Document.COLUMN_DISPLAY_NAME);
-                final String newDocumentId = moveDocument(documentId, targetId);
-
-                if (newDocumentId != null) {
-                    final Uri newDocumentUri = buildDocumentUriMaybeUsingTree(documentUri,
-                            newDocumentId);
-
-                    if (!isTreeUri(newDocumentUri)) {
-                        final int modeFlags = getCallingOrSelfUriPermissionModeFlags(context,
-                                documentUri);
-                        context.grantUriPermission(getCallingPackage(), newDocumentUri, modeFlags);
-                    }
-
-                    out.putParcelable(DocumentsContract.EXTRA_URI, newDocumentUri);
-                }
-
                 // Original document no longer exists, clean up any grants
                 revokeDocumentPermission(documentId);
-
-            } else {
-                throw new UnsupportedOperationException("Method not supported " + method);
             }
-        } catch (FileNotFoundException e) {
-            throw new IllegalStateException("Failed call " + method, e);
+
+        } else if (METHOD_DELETE_DOCUMENT.equals(method)) {
+            enforceWritePermissionInner(documentUri, getCallingPackage(), null);
+            deleteDocument(documentId);
+
+            // Document no longer exists, clean up any grants
+            revokeDocumentPermission(documentId);
+
+        } else if (METHOD_COPY_DOCUMENT.equals(method)) {
+            final Uri targetUri = extras.getParcelable(DocumentsContract.EXTRA_TARGET_URI);
+            final String targetId = DocumentsContract.getDocumentId(targetUri);
+
+            enforceReadPermissionInner(documentUri, getCallingPackage(), null);
+            enforceWritePermissionInner(targetUri, getCallingPackage(), null);
+
+            final String newDocumentId = copyDocument(documentId, targetId);
+
+            if (newDocumentId != null) {
+                final Uri newDocumentUri = buildDocumentUriMaybeUsingTree(documentUri,
+                        newDocumentId);
+
+                if (!isTreeUri(newDocumentUri)) {
+                    final int modeFlags = getCallingOrSelfUriPermissionModeFlags(context,
+                            documentUri);
+                    context.grantUriPermission(getCallingPackage(), newDocumentUri, modeFlags);
+                }
+
+                out.putParcelable(DocumentsContract.EXTRA_URI, newDocumentUri);
+            }
+
+        } else if (METHOD_MOVE_DOCUMENT.equals(method)) {
+            final Uri targetUri = extras.getParcelable(DocumentsContract.EXTRA_TARGET_URI);
+            final String targetId = DocumentsContract.getDocumentId(targetUri);
+
+            enforceReadPermissionInner(documentUri, getCallingPackage(), null);
+            enforceWritePermissionInner(targetUri, getCallingPackage(), null);
+
+            final String newDocumentId = moveDocument(documentId, targetId);
+
+            if (newDocumentId != null) {
+                final Uri newDocumentUri = buildDocumentUriMaybeUsingTree(documentUri,
+                        newDocumentId);
+
+                if (!isTreeUri(newDocumentUri)) {
+                    final int modeFlags = getCallingOrSelfUriPermissionModeFlags(context,
+                            documentUri);
+                    context.grantUriPermission(getCallingPackage(), newDocumentUri, modeFlags);
+                }
+
+                out.putParcelable(DocumentsContract.EXTRA_URI, newDocumentUri);
+            }
+
+            // Original document no longer exists, clean up any grants
+            revokeDocumentPermission(documentId);
+
+        } else {
+            throw new UnsupportedOperationException("Method not supported " + method);
         }
+
         return out;
     }
 
diff --git a/core/java/android/security/net/config/ApplicationConfig.java b/core/java/android/security/net/config/ApplicationConfig.java
index 48359d47..b627641 100644
--- a/core/java/android/security/net/config/ApplicationConfig.java
+++ b/core/java/android/security/net/config/ApplicationConfig.java
@@ -144,18 +144,4 @@
             return sInstance;
         }
     }
-
-    /** @hide */
-    public static ApplicationConfig getPlatformDefault() {
-        return new ApplicationConfig(new ConfigSource() {
-            @Override
-            public NetworkSecurityConfig getDefaultConfig() {
-                return NetworkSecurityConfig.DEFAULT;
-            }
-            @Override
-            public Set<Pair<Domain, NetworkSecurityConfig>> getPerDomainConfigs() {
-                return null;
-            }
-        });
-    }
 }
diff --git a/core/java/android/security/net/config/CertificateSource.java b/core/java/android/security/net/config/CertificateSource.java
index 386354d..2b7829e 100644
--- a/core/java/android/security/net/config/CertificateSource.java
+++ b/core/java/android/security/net/config/CertificateSource.java
@@ -22,4 +22,5 @@
 /** @hide */
 public interface CertificateSource {
     Set<X509Certificate> getCertificates();
+    X509Certificate findBySubjectAndPublicKey(X509Certificate cert);
 }
diff --git a/core/java/android/security/net/config/CertificatesEntryRef.java b/core/java/android/security/net/config/CertificatesEntryRef.java
index 2ba38c21..1d15e19 100644
--- a/core/java/android/security/net/config/CertificatesEntryRef.java
+++ b/core/java/android/security/net/config/CertificatesEntryRef.java
@@ -30,6 +30,10 @@
         mOverridesPins = overridesPins;
     }
 
+    boolean overridesPins() {
+        return mOverridesPins;
+    }
+
     public Set<TrustAnchor> getTrustAnchors() {
         // TODO: cache this [but handle mutable sources]
         Set<TrustAnchor> anchors = new ArraySet<TrustAnchor>();
@@ -38,4 +42,13 @@
         }
         return anchors;
     }
+
+    public TrustAnchor findBySubjectAndPublicKey(X509Certificate cert) {
+        X509Certificate foundCert = mSource.findBySubjectAndPublicKey(cert);
+        if (foundCert == null) {
+            return null;
+        }
+
+        return new TrustAnchor(foundCert, mOverridesPins);
+    }
 }
diff --git a/core/java/android/security/net/config/DirectoryCertificateSource.java b/core/java/android/security/net/config/DirectoryCertificateSource.java
new file mode 100644
index 0000000..a261e06
--- /dev/null
+++ b/core/java/android/security/net/config/DirectoryCertificateSource.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.net.config;
+
+import android.os.Environment;
+import android.os.UserHandle;
+import android.util.ArraySet;
+import android.util.Pair;
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.io.IOException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.Set;
+import libcore.io.IoUtils;
+
+import com.android.org.conscrypt.Hex;
+import com.android.org.conscrypt.NativeCrypto;
+
+import javax.security.auth.x500.X500Principal;
+
+/**
+ * {@link CertificateSource} based on a directory where certificates are stored as individual files
+ * named after a hash of their SubjectName for more efficient lookups.
+ * @hide
+ */
+abstract class DirectoryCertificateSource implements CertificateSource {
+    private final File mDir;
+    private final Object mLock = new Object();
+    private final CertificateFactory mCertFactory;
+
+    private Set<X509Certificate> mCertificates;
+
+    protected DirectoryCertificateSource(File caDir) {
+        mDir = caDir;
+        try {
+            mCertFactory = CertificateFactory.getInstance("X.509");
+        } catch (CertificateException e) {
+            throw new RuntimeException("Failed to obtain X.509 CertificateFactory", e);
+        }
+    }
+
+    protected abstract boolean isCertMarkedAsRemoved(String caFile);
+
+    @Override
+    public Set<X509Certificate> getCertificates() {
+        // TODO: loading all of these is wasteful, we should instead use a keystore style API.
+        synchronized (mLock) {
+            if (mCertificates != null) {
+                return mCertificates;
+            }
+
+            Set<X509Certificate> certs = new ArraySet<X509Certificate>();
+            if (mDir.isDirectory()) {
+                for (String caFile : mDir.list()) {
+                    if (isCertMarkedAsRemoved(caFile)) {
+                        continue;
+                    }
+                    X509Certificate cert = readCertificate(caFile);
+                    if (cert != null) {
+                        certs.add(cert);
+                    }
+                }
+            }
+            mCertificates = certs;
+            return mCertificates;
+        }
+    }
+
+    @Override
+    public X509Certificate findBySubjectAndPublicKey(final X509Certificate cert) {
+        return findCert(cert.getSubjectX500Principal(), new CertSelector() {
+            @Override
+            public boolean match(X509Certificate ca) {
+                return ca.getPublicKey().equals(cert.getPublicKey());
+            }
+        });
+    }
+
+    private static interface CertSelector {
+        boolean match(X509Certificate cert);
+    }
+
+    private X509Certificate findCert(X500Principal subj, CertSelector selector) {
+        String hash = getHash(subj);
+        for (int index = 0; index >= 0; index++) {
+            String fileName = hash + "." + index;
+            if (!new File(mDir, fileName).exists()) {
+                break;
+            }
+            if (isCertMarkedAsRemoved(fileName)) {
+                continue;
+            }
+            X509Certificate cert = readCertificate(fileName);
+            if (!subj.equals(cert.getSubjectX500Principal())) {
+                continue;
+            }
+            if (selector.match(cert)) {
+                return cert;
+            }
+        }
+        return null;
+    }
+
+    private String getHash(X500Principal name) {
+        int hash = NativeCrypto.X509_NAME_hash_old(name);
+        return Hex.intToHexString(hash, 8);
+    }
+
+    private X509Certificate readCertificate(String file) {
+        InputStream is = null;
+        try {
+            is = new BufferedInputStream(new FileInputStream(new File(mDir, file)));
+            return (X509Certificate) mCertFactory.generateCertificate(is);
+        } catch (CertificateException | IOException e) {
+            return null;
+        } finally {
+            IoUtils.closeQuietly(is);
+        }
+    }
+}
diff --git a/core/java/android/security/net/config/KeyStoreCertificateSource.java b/core/java/android/security/net/config/KeyStoreCertificateSource.java
index 9167a90..7a01a64 100644
--- a/core/java/android/security/net/config/KeyStoreCertificateSource.java
+++ b/core/java/android/security/net/config/KeyStoreCertificateSource.java
@@ -24,6 +24,8 @@
 import java.util.Enumeration;
 import java.util.Set;
 
+import com.android.org.conscrypt.TrustedCertificateIndex;
+
 /**
  * {@link CertificateSource} which provides certificates from trusted certificate entries of a
  * {@link KeyStore}.
@@ -31,6 +33,7 @@
 class KeyStoreCertificateSource implements CertificateSource {
     private final Object mLock = new Object();
     private final KeyStore mKeyStore;
+    private TrustedCertificateIndex mIndex;
     private Set<X509Certificate> mCertificates;
 
     public KeyStoreCertificateSource(KeyStore ks) {
@@ -39,24 +42,42 @@
 
     @Override
     public Set<X509Certificate> getCertificates() {
+        ensureInitialized();
+        return mCertificates;
+    }
+
+    private void ensureInitialized() {
         synchronized (mLock) {
             if (mCertificates != null) {
-                return mCertificates;
+                return;
             }
+
             try {
+                TrustedCertificateIndex localIndex = new TrustedCertificateIndex();
                 Set<X509Certificate> certificates = new ArraySet<>(mKeyStore.size());
                 for (Enumeration<String> en = mKeyStore.aliases(); en.hasMoreElements();) {
                     String alias = en.nextElement();
                     X509Certificate cert = (X509Certificate) mKeyStore.getCertificate(alias);
                     if (cert != null) {
                         certificates.add(cert);
+                        localIndex.index(cert);
                     }
                 }
+                mIndex = localIndex;
                 mCertificates = certificates;
-                return mCertificates;
             } catch (KeyStoreException e) {
                 throw new RuntimeException("Failed to load certificates from KeyStore", e);
             }
         }
     }
+
+    @Override
+    public X509Certificate findBySubjectAndPublicKey(X509Certificate cert) {
+        ensureInitialized();
+        java.security.cert.TrustAnchor anchor = mIndex.findBySubjectAndPublicKey(cert);
+        if (anchor == null) {
+            return null;
+        }
+        return anchor.getTrustedCert();
+    }
 }
diff --git a/core/java/android/security/net/config/ManifestConfigSource.java b/core/java/android/security/net/config/ManifestConfigSource.java
new file mode 100644
index 0000000..bf1fb8a
--- /dev/null
+++ b/core/java/android/security/net/config/ManifestConfigSource.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.net.config;
+
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.util.Log;
+import android.util.Pair;
+import java.util.Set;
+
+/** @hide */
+public class ManifestConfigSource implements ConfigSource {
+    public static final String META_DATA_NETWORK_SECURITY_CONFIG =
+            "android.security.net.config";
+    private static final boolean DBG = true;
+    private static final String LOG_TAG = "NetworkSecurityConfig";
+
+    private final Object mLock = new Object();
+    private final Context mContext;
+
+    private ConfigSource mConfigSource;
+
+    public ManifestConfigSource(Context context) {
+        mContext = context;
+    }
+
+    @Override
+    public Set<Pair<Domain, NetworkSecurityConfig>> getPerDomainConfigs() {
+        return getConfigSource().getPerDomainConfigs();
+    }
+
+    @Override
+    public NetworkSecurityConfig getDefaultConfig() {
+        return getConfigSource().getDefaultConfig();
+    }
+
+    private ConfigSource getConfigSource() {
+        synchronized (mLock) {
+            if (mConfigSource != null) {
+                return mConfigSource;
+            }
+            ApplicationInfo info;
+            try {
+                info = mContext.getPackageManager().getApplicationInfo(mContext.getPackageName(),
+                        PackageManager.GET_META_DATA);
+            } catch (PackageManager.NameNotFoundException e) {
+                throw new RuntimeException("Failed to look up ApplicationInfo", e);
+            }
+            int configResourceId = 0;
+            if (info != null && info.metaData != null) {
+                configResourceId = info.metaData.getInt(META_DATA_NETWORK_SECURITY_CONFIG);
+            }
+
+            ConfigSource source;
+            if (configResourceId != 0) {
+                boolean debugBuild = (info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
+                if (DBG) {
+                    Log.d(LOG_TAG, "Using Network Security Config from resource "
+                            + mContext.getResources().getResourceEntryName(configResourceId)
+                            + " debugBuild: " + debugBuild);
+                }
+                source = new XmlConfigSource(mContext, configResourceId, debugBuild);
+            } else {
+                if (DBG) {
+                    Log.d(LOG_TAG, "No Network Security Config specified, using platform default");
+                }
+                source = new DefaultConfigSource();
+            }
+            mConfigSource = source;
+            return mConfigSource;
+        }
+    }
+
+    private static final class DefaultConfigSource implements ConfigSource {
+        @Override
+        public NetworkSecurityConfig getDefaultConfig() {
+            return NetworkSecurityConfig.DEFAULT;
+        }
+
+        @Override
+        public Set<Pair<Domain, NetworkSecurityConfig>> getPerDomainConfigs() {
+            return null;
+        }
+    }
+}
diff --git a/core/java/android/security/net/config/NetworkSecurityConfig.java b/core/java/android/security/net/config/NetworkSecurityConfig.java
index 9eab80c..2ab07b5 100644
--- a/core/java/android/security/net/config/NetworkSecurityConfig.java
+++ b/core/java/android/security/net/config/NetworkSecurityConfig.java
@@ -22,6 +22,7 @@
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.Comparator;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -53,6 +54,19 @@
         mHstsEnforced = hstsEnforced;
         mPins = pins;
         mCertificatesEntryRefs = certificatesEntryRefs;
+        // Sort the certificates entry refs so that all entries that override pins come before
+        // non-override pin entries. This allows us to handle the case where a certificate is in
+        // multiple entry refs by returning the certificate from the first entry ref.
+        Collections.sort(mCertificatesEntryRefs, new Comparator<CertificatesEntryRef>() {
+            @Override
+            public int compare(CertificatesEntryRef lhs, CertificatesEntryRef rhs) {
+                if (lhs.overridesPins()) {
+                    return rhs.overridesPins() ? 0 : -1;
+                } else {
+                    return rhs.overridesPins() ? 1 : 0;
+                }
+            }
+        });
     }
 
     public Set<TrustAnchor> getTrustAnchors() {
@@ -63,14 +77,15 @@
             // Merge trust anchors based on the X509Certificate.
             // If we see the same certificate in two TrustAnchors, one with overridesPins and one
             // without, the one with overridesPins wins.
+            // Because mCertificatesEntryRefs is sorted with all overridesPins anchors coming first
+            // this can be simplified to just using the first occurrence of a certificate.
             Map<X509Certificate, TrustAnchor> anchorMap = new ArrayMap<>();
             for (CertificatesEntryRef ref : mCertificatesEntryRefs) {
                 Set<TrustAnchor> anchors = ref.getTrustAnchors();
                 for (TrustAnchor anchor : anchors) {
-                    if (anchor.overridesPins) {
-                        anchorMap.put(anchor.certificate, anchor);
-                    } else if (!anchorMap.containsKey(anchor.certificate)) {
-                        anchorMap.put(anchor.certificate, anchor);
+                    X509Certificate cert = anchor.certificate;
+                    if (!anchorMap.containsKey(cert)) {
+                        anchorMap.put(cert, anchor);
                     }
                 }
             }
@@ -108,6 +123,17 @@
         }
     }
 
+    /** @hide */
+    public TrustAnchor findTrustAnchorBySubjectAndPublicKey(X509Certificate cert) {
+        for (CertificatesEntryRef ref : mCertificatesEntryRefs) {
+            TrustAnchor anchor = ref.findBySubjectAndPublicKey(cert);
+            if (anchor != null) {
+                return anchor;
+            }
+        }
+        return null;
+    }
+
     /**
      * Return a {@link Builder} for the default {@code NetworkSecurityConfig}.
      *
diff --git a/core/java/android/security/net/config/NetworkSecurityConfigProvider.java b/core/java/android/security/net/config/NetworkSecurityConfigProvider.java
index ac762ef..5ebc7ac 100644
--- a/core/java/android/security/net/config/NetworkSecurityConfigProvider.java
+++ b/core/java/android/security/net/config/NetworkSecurityConfigProvider.java
@@ -17,20 +17,13 @@
 package android.security.net.config;
 
 import android.content.Context;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.util.Log;
 import java.security.Security;
 import java.security.Provider;
 
 /** @hide */
 public final class NetworkSecurityConfigProvider extends Provider {
-    private static final String LOG_TAG = "NetworkSecurityConfig";
     private static final String PREFIX =
             NetworkSecurityConfigProvider.class.getPackage().getName() + ".";
-    public static final String META_DATA_NETWORK_SECURITY_CONFIG =
-            "android.security.net.config";
-    private static final boolean DBG = true;
 
     public NetworkSecurityConfigProvider() {
         // TODO: More clever name than this
@@ -40,36 +33,7 @@
     }
 
     public static void install(Context context) {
-        ApplicationInfo info = null;
-        // TODO: This lookup shouldn't be done in the app startup path, it should be done lazily.
-        try {
-            info = context.getPackageManager().getApplicationInfo(context.getPackageName(),
-                    PackageManager.GET_META_DATA);
-        } catch (PackageManager.NameNotFoundException e) {
-            throw new RuntimeException("Failed to look up ApplicationInfo", e);
-        }
-        int configResourceId = 0;
-        if (info != null && info.metaData != null) {
-            configResourceId = info.metaData.getInt(META_DATA_NETWORK_SECURITY_CONFIG);
-        }
-
-        ApplicationConfig config;
-        if (configResourceId != 0) {
-            boolean debugBuild = (info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
-            if (DBG) {
-                Log.d(LOG_TAG, "Using Network Security Config from resource "
-                        + context.getResources().getResourceEntryName(configResourceId)
-                        + " debugBuild: " + debugBuild);
-            }
-            ConfigSource source = new XmlConfigSource(context, configResourceId, debugBuild);
-            config = new ApplicationConfig(source);
-        } else {
-            if (DBG) {
-                Log.d(LOG_TAG, "No Network Security Config specified, using platform default");
-            }
-            config = ApplicationConfig.getPlatformDefault();
-        }
-
+        ApplicationConfig config = new ApplicationConfig(new ManifestConfigSource(context));
         ApplicationConfig.setDefaultInstance(config);
         int pos = Security.insertProviderAt(new NetworkSecurityConfigProvider(), 1);
         if (pos != 1) {
diff --git a/core/java/android/security/net/config/NetworkSecurityTrustManager.java b/core/java/android/security/net/config/NetworkSecurityTrustManager.java
index 2b860fa..6013c1e 100644
--- a/core/java/android/security/net/config/NetworkSecurityTrustManager.java
+++ b/core/java/android/security/net/config/NetworkSecurityTrustManager.java
@@ -133,14 +133,8 @@
             return false;
         }
         X509Certificate anchorCert = chain.get(chain.size() - 1);
-        TrustAnchor chainAnchor = null;
-        // TODO: faster lookup
-        for (TrustAnchor anchor : mNetworkSecurityConfig.getTrustAnchors()) {
-            if (anchor.certificate.equals(anchorCert)) {
-                chainAnchor = anchor;
-                break;
-            }
-        }
+        TrustAnchor chainAnchor =
+                mNetworkSecurityConfig.findTrustAnchorBySubjectAndPublicKey(anchorCert);
         if (chainAnchor == null) {
             throw new CertificateException("Trusted chain does not end in a TrustAnchor");
         }
diff --git a/core/java/android/security/net/config/ResourceCertificateSource.java b/core/java/android/security/net/config/ResourceCertificateSource.java
index 06dd9d4..b007f8f 100644
--- a/core/java/android/security/net/config/ResourceCertificateSource.java
+++ b/core/java/android/security/net/config/ResourceCertificateSource.java
@@ -27,26 +27,29 @@
 import java.util.Collection;
 import java.util.Set;
 
+import com.android.org.conscrypt.TrustedCertificateIndex;
+
 /**
  * {@link CertificateSource} based on certificates contained in an application resource file.
  * @hide
  */
 public class ResourceCertificateSource implements CertificateSource {
-    private Set<X509Certificate> mCertificates;
-    private final int  mResourceId;
-    private Context mContext;
     private final Object mLock = new Object();
+    private final int  mResourceId;
+
+    private Set<X509Certificate> mCertificates;
+    private Context mContext;
+    private TrustedCertificateIndex mIndex;
 
     public ResourceCertificateSource(int resourceId, Context context) {
         mResourceId = resourceId;
         mContext = context.getApplicationContext();
     }
 
-    @Override
-    public Set<X509Certificate> getCertificates() {
+    private void ensureInitialized() {
         synchronized (mLock) {
             if (mCertificates != null) {
-                return mCertificates;
+                return;
             }
             Set<X509Certificate> certificates = new ArraySet<X509Certificate>();
             Collection<? extends Certificate> certs;
@@ -61,12 +64,30 @@
             } finally {
                 IoUtils.closeQuietly(in);
             }
+            TrustedCertificateIndex indexLocal = new TrustedCertificateIndex();
             for (Certificate cert : certs) {
-                    certificates.add((X509Certificate) cert);
+                certificates.add((X509Certificate) cert);
+                indexLocal.index((X509Certificate) cert);
             }
             mCertificates = certificates;
+            mIndex = indexLocal;
             mContext = null;
-            return mCertificates;
         }
     }
+
+    @Override
+    public Set<X509Certificate> getCertificates() {
+        ensureInitialized();
+        return mCertificates;
+    }
+
+    @Override
+    public X509Certificate findBySubjectAndPublicKey(X509Certificate cert) {
+        ensureInitialized();
+        java.security.cert.TrustAnchor anchor = mIndex.findBySubjectAndPublicKey(cert);
+        if (anchor == null) {
+            return null;
+        }
+        return anchor.getTrustedCert();
+    }
 }
diff --git a/core/java/android/security/net/config/SystemCertificateSource.java b/core/java/android/security/net/config/SystemCertificateSource.java
index 7649a97..abef7b4 100644
--- a/core/java/android/security/net/config/SystemCertificateSource.java
+++ b/core/java/android/security/net/config/SystemCertificateSource.java
@@ -18,29 +18,20 @@
 
 import android.os.Environment;
 import android.os.UserHandle;
-import android.util.ArraySet;
-import java.io.BufferedInputStream;
 import java.io.File;
-import java.io.FileInputStream;
-import java.io.InputStream;
-import java.io.IOException;
-import java.security.cert.Certificate;
-import java.security.cert.CertificateException;
-import java.security.cert.CertificateFactory;
-import java.security.cert.X509Certificate;
-import java.util.Set;
-import libcore.io.IoUtils;
 
 /**
  * {@link CertificateSource} based on the system trusted CA store.
  * @hide
  */
-public class SystemCertificateSource implements CertificateSource {
+public final class SystemCertificateSource extends DirectoryCertificateSource {
     private static final SystemCertificateSource INSTANCE = new SystemCertificateSource();
-    private Set<X509Certificate> mSystemCerts = null;
-    private final Object mLock = new Object();
+    private final File mUserRemovedCaDir;
 
     private SystemCertificateSource() {
+        super(new File(System.getenv("ANDROID_ROOT") + "/etc/security/cacerts"));
+        File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
+        mUserRemovedCaDir = new File(configDir, "cacerts-removed");
     }
 
     public static SystemCertificateSource getInstance() {
@@ -48,54 +39,7 @@
     }
 
     @Override
-    public Set<X509Certificate> getCertificates() {
-        // TODO: loading all of these is wasteful, we should instead use a keystore style API.
-        synchronized (mLock) {
-            if (mSystemCerts != null) {
-                return mSystemCerts;
-            }
-            CertificateFactory certFactory;
-            try {
-                certFactory = CertificateFactory.getInstance("X.509");
-            } catch (CertificateException e) {
-                throw new RuntimeException("Failed to obtain X.509 CertificateFactory", e);
-            }
-
-            final String ANDROID_ROOT = System.getenv("ANDROID_ROOT");
-            final File systemCaDir = new File(ANDROID_ROOT + "/etc/security/cacerts");
-            final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
-            final File userRemovedCaDir = new File(configDir, "cacerts-removed");
-            // Sanity check
-            if (!systemCaDir.isDirectory()) {
-                throw new AssertionError(systemCaDir + " is not a directory");
-            }
-
-            Set<X509Certificate> systemCerts = new ArraySet<X509Certificate>();
-            for (String caFile : systemCaDir.list()) {
-                // Skip any CAs in the user's deleted directory.
-                if (new File(userRemovedCaDir, caFile).exists()) {
-                    continue;
-                }
-                InputStream is = null;
-                try {
-                    is = new BufferedInputStream(
-                            new FileInputStream(new File(systemCaDir, caFile)));
-                    systemCerts.add((X509Certificate) certFactory.generateCertificate(is));
-                } catch (CertificateException | IOException e) {
-                    // Don't rethrow to be consistent with conscrypt's cert loading code.
-                    continue;
-                } finally {
-                    IoUtils.closeQuietly(is);
-                }
-            }
-            mSystemCerts = systemCerts;
-            return mSystemCerts;
-        }
-    }
-
-    public void onCertificateStorageChange() {
-        synchronized (mLock) {
-            mSystemCerts = null;
-        }
+    protected boolean isCertMarkedAsRemoved(String caFile) {
+        return new File(mUserRemovedCaDir, caFile).exists();
     }
 }
diff --git a/core/java/android/security/net/config/UserCertificateSource.java b/core/java/android/security/net/config/UserCertificateSource.java
index e9d5aa1..1a7d924 100644
--- a/core/java/android/security/net/config/UserCertificateSource.java
+++ b/core/java/android/security/net/config/UserCertificateSource.java
@@ -18,29 +18,18 @@
 
 import android.os.Environment;
 import android.os.UserHandle;
-import android.util.ArraySet;
-import java.io.BufferedInputStream;
 import java.io.File;
-import java.io.FileInputStream;
-import java.io.InputStream;
-import java.io.IOException;
-import java.security.cert.Certificate;
-import java.security.cert.CertificateException;
-import java.security.cert.CertificateFactory;
-import java.security.cert.X509Certificate;
-import java.util.Set;
-import libcore.io.IoUtils;
 
 /**
  * {@link CertificateSource} based on the user-installed trusted CA store.
  * @hide
  */
-public class UserCertificateSource implements CertificateSource {
+public final class UserCertificateSource extends DirectoryCertificateSource {
     private static final UserCertificateSource INSTANCE = new UserCertificateSource();
-    private Set<X509Certificate> mUserCerts = null;
-    private final Object mLock = new Object();
 
     private UserCertificateSource() {
+        super(new File(
+                Environment.getUserConfigDirectory(UserHandle.myUserId()), "cacerts-added"));
     }
 
     public static UserCertificateSource getInstance() {
@@ -48,45 +37,7 @@
     }
 
     @Override
-    public Set<X509Certificate> getCertificates() {
-        // TODO: loading all of these is wasteful, we should instead use a keystore style API.
-        synchronized (mLock) {
-            if (mUserCerts != null) {
-                return mUserCerts;
-            }
-            CertificateFactory certFactory;
-            try {
-                certFactory = CertificateFactory.getInstance("X.509");
-            } catch (CertificateException e) {
-                throw new RuntimeException("Failed to obtain X.509 CertificateFactory", e);
-            }
-            final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
-            final File userCaDir = new File(configDir, "cacerts-added");
-            Set<X509Certificate> userCerts = new ArraySet<X509Certificate>();
-            // If the user hasn't added any certificates the directory may not exist.
-            if (userCaDir.isDirectory()) {
-                for (String caFile : userCaDir.list()) {
-                    InputStream is = null;
-                    try {
-                        is = new BufferedInputStream(
-                                new FileInputStream(new File(userCaDir, caFile)));
-                        userCerts.add((X509Certificate) certFactory.generateCertificate(is));
-                    } catch (CertificateException | IOException e) {
-                        // Don't rethrow to be consistent with conscrypt's cert loading code.
-                        continue;
-                    } finally {
-                        IoUtils.closeQuietly(is);
-                    }
-                }
-            }
-            mUserCerts = userCerts;
-            return mUserCerts;
-        }
-    }
-
-    public void onCertificateStorageChange() {
-        synchronized (mLock) {
-            mUserCerts = null;
-        }
+    protected boolean isCertMarkedAsRemoved(String caFile) {
+        return false;
     }
 }
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index b3399d0..541623d 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -1110,7 +1110,7 @@
             if (!Objects.equals(id, to.id)) {
                 d.addLine(item, "id", id, to.id);
             }
-            if (creationTime == to.creationTime) {
+            if (creationTime != to.creationTime) {
                 d.addLine(item, "creationTime", creationTime, to.creationTime);
             }
         }
diff --git a/core/java/android/text/Hyphenator.java b/core/java/android/text/Hyphenator.java
index f2b6041..5d9d929 100644
--- a/core/java/android/text/Hyphenator.java
+++ b/core/java/android/text/Hyphenator.java
@@ -166,7 +166,7 @@
         sMap.put(null, null);
 
         // TODO: replace this with a discovery-based method that looks into /system/usr/hyphen-data
-        String[] availableLanguages = {"en-US", "eu", "hu", "hy", "nb", "nn", "und-Ethi"};
+        String[] availableLanguages = {"en-US", "es", "eu", "hu", "hy", "nb", "nn", "und-Ethi"};
         for (int i = 0; i < availableLanguages.length; i++) {
             String languageTag = availableLanguages[i];
             Hyphenator h = loadHyphenator(languageTag);
diff --git a/core/java/android/view/HardwareLayer.java b/core/java/android/view/HardwareLayer.java
index 692ca7b..a12434c 100644
--- a/core/java/android/view/HardwareLayer.java
+++ b/core/java/android/view/HardwareLayer.java
@@ -33,10 +33,10 @@
  * @hide
  */
 final class HardwareLayer {
-    private HardwareRenderer mRenderer;
+    private ThreadedRenderer mRenderer;
     private VirtualRefBasePtr mFinalizer;
 
-    private HardwareLayer(HardwareRenderer renderer, long deferredUpdater) {
+    private HardwareLayer(ThreadedRenderer renderer, long deferredUpdater) {
         if (renderer == null || deferredUpdater == 0) {
             throw new IllegalArgumentException("Either hardware renderer: " + renderer
                     + " or deferredUpdater: " + deferredUpdater + " is invalid");
@@ -140,7 +140,7 @@
         mRenderer.pushLayerUpdate(this);
     }
 
-    static HardwareLayer adoptTextureLayer(HardwareRenderer renderer, long layer) {
+    static HardwareLayer adoptTextureLayer(ThreadedRenderer renderer, long layer) {
         return new HardwareLayer(renderer, layer);
     }
 
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
deleted file mode 100644
index 5e58250..0000000
--- a/core/java/android/view/HardwareRenderer.java
+++ /dev/null
@@ -1,510 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view;
-
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.Rect;
-import android.view.Surface.OutOfResourcesException;
-
-import java.io.File;
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-
-/**
- * Interface for rendering a view hierarchy using hardware acceleration.
- *
- * @hide
- */
-public abstract class HardwareRenderer {
-    static final String LOG_TAG = "HardwareRenderer";
-
-    /**
-     * Name of the file that holds the shaders cache.
-     */
-    private static final String CACHE_PATH_SHADERS = "com.android.opengl.shaders_cache";
-
-    /**
-     * System property used to enable or disable dirty regions invalidation.
-     * This property is only queried if {@link #RENDER_DIRTY_REGIONS} is true.
-     * The default value of this property is assumed to be true.
-     *
-     * Possible values:
-     * "true", to enable partial invalidates
-     * "false", to disable partial invalidates
-     */
-    static final String RENDER_DIRTY_REGIONS_PROPERTY = "debug.hwui.render_dirty_regions";
-
-    /**
-     * System property used to enable or disable hardware rendering profiling.
-     * The default value of this property is assumed to be false.
-     *
-     * When profiling is enabled, the adb shell dumpsys gfxinfo command will
-     * output extra information about the time taken to execute by the last
-     * frames.
-     *
-     * Possible values:
-     * "true", to enable profiling
-     * "visual_bars", to enable profiling and visualize the results on screen
-     * "false", to disable profiling
-     *
-     * @see #PROFILE_PROPERTY_VISUALIZE_BARS
-     *
-     * @hide
-     */
-    public static final String PROFILE_PROPERTY = "debug.hwui.profile";
-
-    /**
-     * Value for {@link #PROFILE_PROPERTY}. When the property is set to this
-     * value, profiling data will be visualized on screen as a bar chart.
-     *
-     * @hide
-     */
-    public static final String PROFILE_PROPERTY_VISUALIZE_BARS = "visual_bars";
-
-    /**
-     * System property used to specify the number of frames to be used
-     * when doing hardware rendering profiling.
-     * The default value of this property is #PROFILE_MAX_FRAMES.
-     *
-     * When profiling is enabled, the adb shell dumpsys gfxinfo command will
-     * output extra information about the time taken to execute by the last
-     * frames.
-     *
-     * Possible values:
-     * "60", to set the limit of frames to 60
-     */
-    static final String PROFILE_MAXFRAMES_PROPERTY = "debug.hwui.profile.maxframes";
-
-    /**
-     * System property used to debug EGL configuration choice.
-     *
-     * Possible values:
-     * "choice", print the chosen configuration only
-     * "all", print all possible configurations
-     */
-    static final String PRINT_CONFIG_PROPERTY = "debug.hwui.print_config";
-
-    /**
-     * Turn on to draw dirty regions every other frame.
-     *
-     * Possible values:
-     * "true", to enable dirty regions debugging
-     * "false", to disable dirty regions debugging
-     *
-     * @hide
-     */
-    public static final String DEBUG_DIRTY_REGIONS_PROPERTY = "debug.hwui.show_dirty_regions";
-
-    /**
-     * Turn on to flash hardware layers when they update.
-     *
-     * Possible values:
-     * "true", to enable hardware layers updates debugging
-     * "false", to disable hardware layers updates debugging
-     *
-     * @hide
-     */
-    public static final String DEBUG_SHOW_LAYERS_UPDATES_PROPERTY =
-            "debug.hwui.show_layers_updates";
-
-    /**
-     * Controls overdraw debugging.
-     *
-     * Possible values:
-     * "false", to disable overdraw debugging
-     * "show", to show overdraw areas on screen
-     * "count", to display an overdraw counter
-     *
-     * @hide
-     */
-    public static final String DEBUG_OVERDRAW_PROPERTY = "debug.hwui.overdraw";
-
-    /**
-     * Value for {@link #DEBUG_OVERDRAW_PROPERTY}. When the property is set to this
-     * value, overdraw will be shown on screen by coloring pixels.
-     *
-     * @hide
-     */
-    public static final String OVERDRAW_PROPERTY_SHOW = "show";
-
-    /**
-     * Turn on to debug non-rectangular clip operations.
-     *
-     * Possible values:
-     * "hide", to disable this debug mode
-     * "highlight", highlight drawing commands tested against a non-rectangular clip
-     * "stencil", renders the clip region on screen when set
-     *
-     * @hide
-     */
-    public static final String DEBUG_SHOW_NON_RECTANGULAR_CLIP_PROPERTY =
-            "debug.hwui.show_non_rect_clip";
-
-    /**
-     * A process can set this flag to false to prevent the use of hardware
-     * rendering.
-     *
-     * @hide
-     */
-    public static boolean sRendererDisabled = false;
-
-    /**
-     * Further hardware renderer disabling for the system process.
-     *
-     * @hide
-     */
-    public static boolean sSystemRendererDisabled = false;
-
-    private boolean mEnabled;
-    private boolean mRequested = true;
-
-    /**
-     * Invoke this method to disable hardware rendering in the current process.
-     *
-     * @hide
-     */
-    public static void disable(boolean system) {
-        sRendererDisabled = true;
-        if (system) {
-            sSystemRendererDisabled = true;
-        }
-    }
-
-    public static boolean sTrimForeground = false;
-
-    /**
-     * Controls whether or not the hardware renderer should aggressively
-     * trim memory. Note that this must not be set for any process that
-     * uses WebView! This should be only used by system_process or similar
-     * that do not go into the background.
-     */
-    public static void enableForegroundTrimming() {
-        sTrimForeground = true;
-    }
-
-    /**
-     * Indicates whether hardware acceleration is available under any form for
-     * the view hierarchy.
-     *
-     * @return True if the view hierarchy can potentially be hardware accelerated,
-     *         false otherwise
-     */
-    public static boolean isAvailable() {
-        return DisplayListCanvas.isAvailable();
-    }
-
-    /**
-     * Destroys the hardware rendering context.
-     */
-    abstract void destroy();
-
-    /**
-     * Initializes the hardware renderer for the specified surface.
-     *
-     * @param surface The surface to hardware accelerate
-     *
-     * @return True if the initialization was successful, false otherwise.
-     */
-    abstract boolean initialize(Surface surface) throws OutOfResourcesException;
-
-    /**
-     * Updates the hardware renderer for the specified surface.
-     *
-     * @param surface The surface to hardware accelerate
-     */
-    abstract void updateSurface(Surface surface) throws OutOfResourcesException;
-
-    /**
-     * Stops any rendering into the surface. Use this if it is unclear whether
-     * or not the surface used by the HardwareRenderer will be changing. It
-     * Suspends any rendering into the surface, but will not do any destruction
-     */
-    abstract boolean pauseSurface(Surface surface);
-
-    /**
-     * Destroys all hardware rendering resources associated with the specified
-     * view hierarchy.
-     *
-     * @param view The root of the view hierarchy
-     */
-    abstract void destroyHardwareResources(View view);
-
-    /**
-     * This method should be invoked whenever the current hardware renderer
-     * context should be reset.
-     *
-     * @param surface The surface to hardware accelerate
-     */
-    abstract void invalidate(Surface surface);
-
-    /**
-     * Detaches the layer's surface texture from the GL context and releases
-     * the texture id
-     */
-    abstract void detachSurfaceTexture(long hardwareLayer);
-
-    /**
-     * Gets the current width of the surface. This is the width that the surface
-     * was last set to in a call to {@link #setup(int, int, View.AttachInfo, Rect)}.
-     *
-     * @return the current width of the surface
-     */
-    abstract int getWidth();
-
-    /**
-     * Gets the current height of the surface. This is the height that the surface
-     * was last set to in a call to {@link #setup(int, int, View.AttachInfo, Rect)}.
-     *
-     * @return the current width of the surface
-     */
-    abstract int getHeight();
-
-    /**
-     * Outputs extra debugging information in the specified file descriptor.
-     */
-    abstract void dumpGfxInfo(PrintWriter pw, FileDescriptor fd, String[] args);
-
-    /**
-     * Loads system properties used by the renderer. This method is invoked
-     * whenever system properties are modified. Implementations can use this
-     * to trigger live updates of the renderer based on properties.
-     *
-     * @return True if a property has changed.
-     */
-    abstract boolean loadSystemProperties();
-
-    /**
-     * Sets the directory to use as a persistent storage for hardware rendering
-     * resources.
-     *
-     * @param cacheDir A directory the current process can write to
-     *
-     * @hide
-     */
-    public static void setupDiskCache(File cacheDir) {
-        ThreadedRenderer.setupShadersDiskCache(new File(cacheDir, CACHE_PATH_SHADERS).getAbsolutePath());
-    }
-
-    /**
-     * Indicates that the specified hardware layer needs to be updated
-     * as soon as possible.
-     *
-     * @param layer The hardware layer that needs an update
-     */
-    abstract void pushLayerUpdate(HardwareLayer layer);
-
-    /**
-     * Tells the HardwareRenderer that the layer is destroyed. The renderer
-     * should remove the layer from any update queues.
-     */
-    abstract void onLayerDestroyed(HardwareLayer layer);
-
-    /**
-     * Interface used to receive callbacks whenever a view is drawn by
-     * a hardware renderer instance.
-     */
-    interface HardwareDrawCallbacks {
-        /**
-         * Invoked before a view is drawn by a hardware renderer.
-         * This method can be used to apply transformations to the
-         * canvas but no drawing command should be issued.
-         *
-         * @param canvas The Canvas used to render the view.
-         */
-        void onHardwarePreDraw(DisplayListCanvas canvas);
-
-        /**
-         * Invoked after a view is drawn by a hardware renderer.
-         * It is safe to invoke drawing commands from this method.
-         *
-         * @param canvas The Canvas used to render the view.
-         */
-        void onHardwarePostDraw(DisplayListCanvas canvas);
-    }
-
-    /**
-     *  Indicates that the content drawn by HardwareDrawCallbacks needs to
-     *  be updated, which will be done by the next call to draw()
-     */
-    abstract void invalidateRoot();
-
-    /**
-     * Draws the specified view.
-     *
-     * @param view The view to draw.
-     * @param attachInfo AttachInfo tied to the specified view.
-     * @param callbacks Callbacks invoked when drawing happens.
-     */
-    abstract void draw(View view, View.AttachInfo attachInfo, HardwareDrawCallbacks callbacks);
-
-    /**
-     * Creates a new hardware layer. A hardware layer built by calling this
-     * method will be treated as a texture layer, instead of as a render target.
-     *
-     * @return A hardware layer
-     */
-    abstract HardwareLayer createTextureLayer();
-
-    abstract void buildLayer(RenderNode node);
-
-    abstract boolean copyLayerInto(HardwareLayer layer, Bitmap bitmap);
-
-    /**
-     * Initializes the hardware renderer for the specified surface and setup the
-     * renderer for drawing, if needed. This is invoked when the ViewAncestor has
-     * potentially lost the hardware renderer. The hardware renderer should be
-     * reinitialized and setup when the render {@link #isRequested()} and
-     * {@link #isEnabled()}.
-     *
-     * @param width The width of the drawing surface.
-     * @param height The height of the drawing surface.
-     * @param attachInfo Information about the window.
-     * @param surface The surface to hardware accelerate
-     * @param surfaceInsets The drawing surface insets to apply
-     *
-     * @return true if the surface was initialized, false otherwise. Returning
-     *         false might mean that the surface was already initialized.
-     */
-    boolean initializeIfNeeded(int width, int height, View.AttachInfo attachInfo,
-            Surface surface, Rect surfaceInsets) throws OutOfResourcesException {
-        if (isRequested()) {
-            // We lost the gl context, so recreate it.
-            if (!isEnabled()) {
-                if (initialize(surface)) {
-                    setup(width, height, attachInfo, surfaceInsets);
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Sets up the renderer for drawing.
-     *
-     * @param width The width of the drawing surface.
-     * @param height The height of the drawing surface.
-     * @param attachInfo Information about the window.
-     * @param surfaceInsets The drawing surface insets to apply
-     */
-    abstract void setup(int width, int height, View.AttachInfo attachInfo, Rect surfaceInsets);
-
-    /**
-     * Updates the light position based on the position of the window.
-     *
-     * @param attachInfo Information about the window.
-     */
-    abstract void setLightCenter(View.AttachInfo attachInfo);
-
-    /**
-     * Optional, sets the name of the renderer. Useful for debugging purposes.
-     *
-     * @param name The name of this renderer, can be null
-     */
-    abstract void setName(String name);
-
-    /**
-     * Change the HardwareRenderer's opacity
-     */
-    abstract void setOpaque(boolean opaque);
-
-    /**
-     * Creates a hardware renderer using OpenGL.
-     *
-     * @param translucent True if the surface is translucent, false otherwise
-     *
-     * @return A hardware renderer backed by OpenGL.
-     */
-    static HardwareRenderer create(Context context, boolean translucent) {
-        HardwareRenderer renderer = null;
-        if (DisplayListCanvas.isAvailable()) {
-            renderer = new ThreadedRenderer(context, translucent);
-        }
-        return renderer;
-    }
-
-    /**
-     * Invoke this method when the system is running out of memory. This
-     * method will attempt to recover as much memory as possible, based on
-     * the specified hint.
-     *
-     * @param level Hint about the amount of memory that should be trimmed,
-     *              see {@link android.content.ComponentCallbacks}
-     */
-    static void trimMemory(int level) {
-        ThreadedRenderer.trimMemory(level);
-    }
-
-    /**
-     * Indicates whether hardware acceleration is currently enabled.
-     *
-     * @return True if hardware acceleration is in use, false otherwise.
-     */
-    boolean isEnabled() {
-        return mEnabled;
-    }
-
-    /**
-     * Indicates whether hardware acceleration is currently enabled.
-     *
-     * @param enabled True if the hardware renderer is in use, false otherwise.
-     */
-    void setEnabled(boolean enabled) {
-        mEnabled = enabled;
-    }
-
-    /**
-     * Indicates whether hardware acceleration is currently request but not
-     * necessarily enabled yet.
-     *
-     * @return True if requested, false otherwise.
-     */
-    boolean isRequested() {
-        return mRequested;
-    }
-
-    /**
-     * Indicates whether hardware acceleration is currently requested but not
-     * necessarily enabled yet.
-     *
-     * @return True to request hardware acceleration, false otherwise.
-     */
-    void setRequested(boolean requested) {
-        mRequested = requested;
-    }
-
-    /**
-     * Blocks until all previously queued work has completed.
-     */
-    abstract void fence();
-
-    /**
-     * Prevents any further drawing until draw() is called. This is a signal
-     * that the contents of the RenderNode tree are no longer safe to play back.
-     * In practice this usually means that there are Functor pointers in the
-     * display list that are no longer valid.
-     */
-    abstract void stopDrawing();
-
-    /**
-     * Called by {@link ViewRootImpl} when a new performTraverals is scheduled.
-     */
-    abstract void notifyFramePending();
-
-    abstract void registerAnimatingRenderNode(RenderNode animator);
-}
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 304e9c0..f2a4d7b 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -35,6 +35,7 @@
 
 import com.android.internal.R;
 
+import java.io.File;
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
@@ -59,8 +60,230 @@
  *
  * @hide
  */
-public class ThreadedRenderer extends HardwareRenderer {
-    private static final String LOGTAG = "ThreadedRenderer";
+public final class ThreadedRenderer {
+    private static final String LOG_TAG = "ThreadedRenderer";
+
+    /**
+     * Name of the file that holds the shaders cache.
+     */
+    private static final String CACHE_PATH_SHADERS = "com.android.opengl.shaders_cache";
+
+    /**
+     * System property used to enable or disable dirty regions invalidation.
+     * This property is only queried if {@link #RENDER_DIRTY_REGIONS} is true.
+     * The default value of this property is assumed to be true.
+     *
+     * Possible values:
+     * "true", to enable partial invalidates
+     * "false", to disable partial invalidates
+     */
+    static final String RENDER_DIRTY_REGIONS_PROPERTY = "debug.hwui.render_dirty_regions";
+
+    /**
+     * System property used to enable or disable hardware rendering profiling.
+     * The default value of this property is assumed to be false.
+     *
+     * When profiling is enabled, the adb shell dumpsys gfxinfo command will
+     * output extra information about the time taken to execute by the last
+     * frames.
+     *
+     * Possible values:
+     * "true", to enable profiling
+     * "visual_bars", to enable profiling and visualize the results on screen
+     * "false", to disable profiling
+     *
+     * @see #PROFILE_PROPERTY_VISUALIZE_BARS
+     *
+     * @hide
+     */
+    public static final String PROFILE_PROPERTY = "debug.hwui.profile";
+
+    /**
+     * Value for {@link #PROFILE_PROPERTY}. When the property is set to this
+     * value, profiling data will be visualized on screen as a bar chart.
+     *
+     * @hide
+     */
+    public static final String PROFILE_PROPERTY_VISUALIZE_BARS = "visual_bars";
+
+    /**
+     * System property used to specify the number of frames to be used
+     * when doing hardware rendering profiling.
+     * The default value of this property is #PROFILE_MAX_FRAMES.
+     *
+     * When profiling is enabled, the adb shell dumpsys gfxinfo command will
+     * output extra information about the time taken to execute by the last
+     * frames.
+     *
+     * Possible values:
+     * "60", to set the limit of frames to 60
+     */
+    static final String PROFILE_MAXFRAMES_PROPERTY = "debug.hwui.profile.maxframes";
+
+    /**
+     * System property used to debug EGL configuration choice.
+     *
+     * Possible values:
+     * "choice", print the chosen configuration only
+     * "all", print all possible configurations
+     */
+    static final String PRINT_CONFIG_PROPERTY = "debug.hwui.print_config";
+
+    /**
+     * Turn on to draw dirty regions every other frame.
+     *
+     * Possible values:
+     * "true", to enable dirty regions debugging
+     * "false", to disable dirty regions debugging
+     *
+     * @hide
+     */
+    public static final String DEBUG_DIRTY_REGIONS_PROPERTY = "debug.hwui.show_dirty_regions";
+
+    /**
+     * Turn on to flash hardware layers when they update.
+     *
+     * Possible values:
+     * "true", to enable hardware layers updates debugging
+     * "false", to disable hardware layers updates debugging
+     *
+     * @hide
+     */
+    public static final String DEBUG_SHOW_LAYERS_UPDATES_PROPERTY =
+            "debug.hwui.show_layers_updates";
+
+    /**
+     * Controls overdraw debugging.
+     *
+     * Possible values:
+     * "false", to disable overdraw debugging
+     * "show", to show overdraw areas on screen
+     * "count", to display an overdraw counter
+     *
+     * @hide
+     */
+    public static final String DEBUG_OVERDRAW_PROPERTY = "debug.hwui.overdraw";
+
+    /**
+     * Value for {@link #DEBUG_OVERDRAW_PROPERTY}. When the property is set to this
+     * value, overdraw will be shown on screen by coloring pixels.
+     *
+     * @hide
+     */
+    public static final String OVERDRAW_PROPERTY_SHOW = "show";
+
+    /**
+     * Turn on to debug non-rectangular clip operations.
+     *
+     * Possible values:
+     * "hide", to disable this debug mode
+     * "highlight", highlight drawing commands tested against a non-rectangular clip
+     * "stencil", renders the clip region on screen when set
+     *
+     * @hide
+     */
+    public static final String DEBUG_SHOW_NON_RECTANGULAR_CLIP_PROPERTY =
+            "debug.hwui.show_non_rect_clip";
+
+    /**
+     * A process can set this flag to false to prevent the use of hardware
+     * rendering.
+     *
+     * @hide
+     */
+    public static boolean sRendererDisabled = false;
+
+    /**
+     * Further hardware renderer disabling for the system process.
+     *
+     * @hide
+     */
+    public static boolean sSystemRendererDisabled = false;
+
+    /**
+     * Invoke this method to disable hardware rendering in the current process.
+     *
+     * @hide
+     */
+    public static void disable(boolean system) {
+        sRendererDisabled = true;
+        if (system) {
+            sSystemRendererDisabled = true;
+        }
+    }
+
+    public static boolean sTrimForeground = false;
+
+    /**
+     * Controls whether or not the hardware renderer should aggressively
+     * trim memory. Note that this must not be set for any process that
+     * uses WebView! This should be only used by system_process or similar
+     * that do not go into the background.
+     */
+    public static void enableForegroundTrimming() {
+        sTrimForeground = true;
+    }
+
+    /**
+     * Indicates whether hardware acceleration is available under any form for
+     * the view hierarchy.
+     *
+     * @return True if the view hierarchy can potentially be hardware accelerated,
+     *         false otherwise
+     */
+    public static boolean isAvailable() {
+        return DisplayListCanvas.isAvailable();
+    }
+
+    /**
+     * Sets the directory to use as a persistent storage for hardware rendering
+     * resources.
+     *
+     * @param cacheDir A directory the current process can write to
+     *
+     * @hide
+     */
+    public static void setupDiskCache(File cacheDir) {
+        ThreadedRenderer.setupShadersDiskCache(new File(cacheDir, CACHE_PATH_SHADERS).getAbsolutePath());
+    }
+
+    /**
+     * Creates a hardware renderer using OpenGL.
+     *
+     * @param translucent True if the surface is translucent, false otherwise
+     *
+     * @return A hardware renderer backed by OpenGL.
+     */
+    public static ThreadedRenderer create(Context context, boolean translucent) {
+        ThreadedRenderer renderer = null;
+        if (DisplayListCanvas.isAvailable()) {
+            renderer = new ThreadedRenderer(context, translucent);
+        }
+        return renderer;
+    }
+
+    /**
+     * Invoke this method when the system is running out of memory. This
+     * method will attempt to recover as much memory as possible, based on
+     * the specified hint.
+     *
+     * @param level Hint about the amount of memory that should be trimmed,
+     *              see {@link android.content.ComponentCallbacks}
+     */
+    public static void trimMemory(int level) {
+        nTrimMemory(level);
+    }
+
+    public static void overrideProperty(@NonNull String name, @NonNull String value) {
+        if (name == null || value == null) {
+            throw new IllegalArgumentException("name and value must be non-null");
+        }
+        nOverrideProperty(name, value);
+    }
+
+    public static void dumpProfileData(byte[] data, FileDescriptor fd) {
+        nDumpProfileData(data, fd);
+    }
 
     // Keep in sync with DrawFrameTask.h SYNC_* flags
     // Nothing interesting to report
@@ -113,6 +336,9 @@
     private final Rect mCurrentContentBounds = new Rect();
     private final Rect mStagedContentBounds = new Rect();
 
+    private boolean mEnabled;
+    private boolean mRequested = true;
+
     ThreadedRenderer(Context context, boolean translucent) {
         final TypedArray a = context.obtainStyledAttributes(null, R.styleable.Lighting, 0, 0);
         mLightY = a.getDimension(R.styleable.Lighting_lightY, 0);
@@ -133,13 +359,53 @@
         loadSystemProperties();
     }
 
-    @Override
+    /**
+     * Destroys the hardware rendering context.
+     */
     void destroy() {
         mInitialized = false;
         updateEnabledState(null);
         nDestroy(mNativeProxy);
     }
 
+    /**
+     * Indicates whether hardware acceleration is currently enabled.
+     *
+     * @return True if hardware acceleration is in use, false otherwise.
+     */
+    boolean isEnabled() {
+        return mEnabled;
+    }
+
+    /**
+     * Indicates whether hardware acceleration is currently enabled.
+     *
+     * @param enabled True if the hardware renderer is in use, false otherwise.
+     */
+    void setEnabled(boolean enabled) {
+        mEnabled = enabled;
+    }
+
+    /**
+     * Indicates whether hardware acceleration is currently request but not
+     * necessarily enabled yet.
+     *
+     * @return True if requested, false otherwise.
+     */
+    boolean isRequested() {
+        return mRequested;
+    }
+
+    /**
+     * Indicates whether hardware acceleration is currently requested but not
+     * necessarily enabled yet.
+     *
+     * @return True to request hardware acceleration, false otherwise.
+     */
+    void setRequested(boolean requested) {
+        mRequested = requested;
+    }
+
     private void updateEnabledState(Surface surface) {
         if (surface == null || !surface.isValid()) {
             setEnabled(false);
@@ -148,7 +414,13 @@
         }
     }
 
-    @Override
+    /**
+     * Initializes the hardware renderer for the specified surface.
+     *
+     * @param surface The surface to hardware accelerate
+     *
+     * @return True if the initialization was successful, false otherwise.
+     */
     boolean initialize(Surface surface) throws OutOfResourcesException {
         mInitialized = true;
         updateEnabledState(surface);
@@ -156,18 +428,61 @@
         return status;
     }
 
-    @Override
+    /**
+     * Initializes the hardware renderer for the specified surface and setup the
+     * renderer for drawing, if needed. This is invoked when the ViewAncestor has
+     * potentially lost the hardware renderer. The hardware renderer should be
+     * reinitialized and setup when the render {@link #isRequested()} and
+     * {@link #isEnabled()}.
+     *
+     * @param width The width of the drawing surface.
+     * @param height The height of the drawing surface.
+     * @param attachInfo Information about the window.
+     * @param surface The surface to hardware accelerate
+     * @param surfaceInsets The drawing surface insets to apply
+     *
+     * @return true if the surface was initialized, false otherwise. Returning
+     *         false might mean that the surface was already initialized.
+     */
+    boolean initializeIfNeeded(int width, int height, View.AttachInfo attachInfo,
+            Surface surface, Rect surfaceInsets) throws OutOfResourcesException {
+        if (isRequested()) {
+            // We lost the gl context, so recreate it.
+            if (!isEnabled()) {
+                if (initialize(surface)) {
+                    setup(width, height, attachInfo, surfaceInsets);
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Updates the hardware renderer for the specified surface.
+     *
+     * @param surface The surface to hardware accelerate
+     */
     void updateSurface(Surface surface) throws OutOfResourcesException {
         updateEnabledState(surface);
         nUpdateSurface(mNativeProxy, surface);
     }
 
-    @Override
+    /**
+     * Stops any rendering into the surface. Use this if it is unclear whether
+     * or not the surface used by the HardwareRenderer will be changing. It
+     * Suspends any rendering into the surface, but will not do any destruction
+     */
     boolean pauseSurface(Surface surface) {
         return nPauseSurface(mNativeProxy, surface);
     }
 
-    @Override
+    /**
+     * Destroys all hardware rendering resources associated with the specified
+     * view hierarchy.
+     *
+     * @param view The root of the view hierarchy
+     */
     void destroyHardwareResources(View view) {
         destroyResources(view);
         nDestroyHardwareResources(mNativeProxy);
@@ -186,17 +501,32 @@
         }
     }
 
-    @Override
+    /**
+     * This method should be invoked whenever the current hardware renderer
+     * context should be reset.
+     *
+     * @param surface The surface to hardware accelerate
+     */
     void invalidate(Surface surface) {
         updateSurface(surface);
     }
 
-    @Override
+    /**
+     * Detaches the layer's surface texture from the GL context and releases
+     * the texture id
+     */
     void detachSurfaceTexture(long hardwareLayer) {
         nDetachSurfaceTexture(mNativeProxy, hardwareLayer);
     }
 
-    @Override
+    /**
+     * Sets up the renderer for drawing.
+     *
+     * @param width The width of the drawing surface.
+     * @param height The height of the drawing surface.
+     * @param attachInfo Information about the window.
+     * @param surfaceInsets The drawing surface insets to apply
+     */
     void setup(int width, int height, AttachInfo attachInfo, Rect surfaceInsets) {
         mWidth = width;
         mHeight = height;
@@ -226,7 +556,11 @@
         setLightCenter(attachInfo);
     }
 
-    @Override
+    /**
+     * Updates the light position based on the position of the window.
+     *
+     * @param attachInfo Information about the window.
+     */
     void setLightCenter(AttachInfo attachInfo) {
         // Adjust light position for window offsets.
         final Point displaySize = attachInfo.mPoint;
@@ -237,22 +571,36 @@
         nSetLightCenter(mNativeProxy, lightX, lightY, mLightZ);
     }
 
-    @Override
+    /**
+     * Change the HardwareRenderer's opacity
+     */
     void setOpaque(boolean opaque) {
         nSetOpaque(mNativeProxy, opaque && !mHasInsets);
     }
 
-    @Override
+    /**
+     * Gets the current width of the surface. This is the width that the surface
+     * was last set to in a call to {@link #setup(int, int, View.AttachInfo, Rect)}.
+     *
+     * @return the current width of the surface
+     */
     int getWidth() {
         return mWidth;
     }
 
-    @Override
+    /**
+     * Gets the current height of the surface. This is the height that the surface
+     * was last set to in a call to {@link #setup(int, int, View.AttachInfo, Rect)}.
+     *
+     * @return the current width of the surface
+     */
     int getHeight() {
         return mHeight;
     }
 
-    @Override
+    /**
+     * Outputs extra debugging information in the specified file descriptor.
+     */
     void dumpGfxInfo(PrintWriter pw, FileDescriptor fd, String[] args) {
         pw.flush();
         int flags = 0;
@@ -269,7 +617,13 @@
         nDumpProfileInfo(mNativeProxy, fd, flags);
     }
 
-    @Override
+    /**
+     * Loads system properties used by the renderer. This method is invoked
+     * whenever system properties are modified. Implementations can use this
+     * to trigger live updates of the renderer based on properties.
+     *
+     * @return True if a property has changed.
+     */
     boolean loadSystemProperties() {
         boolean changed = nLoadSystemProperties(mNativeProxy);
         if (changed) {
@@ -353,12 +707,44 @@
         mStagedContentBounds.set(left, top, right, bottom);
     }
 
-    @Override
+    /**
+     * Interface used to receive callbacks whenever a view is drawn by
+     * a hardware renderer instance.
+     */
+    interface HardwareDrawCallbacks {
+        /**
+         * Invoked before a view is drawn by a hardware renderer.
+         * This method can be used to apply transformations to the
+         * canvas but no drawing command should be issued.
+         *
+         * @param canvas The Canvas used to render the view.
+         */
+        void onHardwarePreDraw(DisplayListCanvas canvas);
+
+        /**
+         * Invoked after a view is drawn by a hardware renderer.
+         * It is safe to invoke drawing commands from this method.
+         *
+         * @param canvas The Canvas used to render the view.
+         */
+        void onHardwarePostDraw(DisplayListCanvas canvas);
+    }
+
+    /**
+     *  Indicates that the content drawn by HardwareDrawCallbacks needs to
+     *  be updated, which will be done by the next call to draw()
+     */
     void invalidateRoot() {
         mRootNodeNeedsUpdate = true;
     }
 
-    @Override
+    /**
+     * Draws the specified view.
+     *
+     * @param view The view to draw.
+     * @param attachInfo AttachInfo tied to the specified view.
+     * @param callbacks Callbacks invoked when drawing happens.
+     */
     void draw(View view, AttachInfo attachInfo, HardwareDrawCallbacks callbacks) {
         attachInfo.mIgnoreDirtyState = true;
 
@@ -409,54 +795,80 @@
         nInvokeFunctor(functor, waitForCompletion);
     }
 
-    @Override
+    /**
+     * Creates a new hardware layer. A hardware layer built by calling this
+     * method will be treated as a texture layer, instead of as a render target.
+     *
+     * @return A hardware layer
+     */
     HardwareLayer createTextureLayer() {
         long layer = nCreateTextureLayer(mNativeProxy);
         return HardwareLayer.adoptTextureLayer(this, layer);
     }
 
-    @Override
+
     void buildLayer(RenderNode node) {
         nBuildLayer(mNativeProxy, node.getNativeDisplayList());
     }
 
-    @Override
+
     boolean copyLayerInto(final HardwareLayer layer, final Bitmap bitmap) {
         return nCopyLayerInto(mNativeProxy,
                 layer.getDeferredLayerUpdater(), bitmap);
     }
 
-    @Override
+    /**
+     * Indicates that the specified hardware layer needs to be updated
+     * as soon as possible.
+     *
+     * @param layer The hardware layer that needs an update
+     */
     void pushLayerUpdate(HardwareLayer layer) {
         nPushLayerUpdate(mNativeProxy, layer.getDeferredLayerUpdater());
     }
 
-    @Override
+    /**
+     * Tells the HardwareRenderer that the layer is destroyed. The renderer
+     * should remove the layer from any update queues.
+     */
     void onLayerDestroyed(HardwareLayer layer) {
         nCancelLayerUpdate(mNativeProxy, layer.getDeferredLayerUpdater());
     }
 
-    @Override
+    /**
+     * Optional, sets the name of the renderer. Useful for debugging purposes.
+     *
+     * @param name The name of this renderer, can be null
+     */
     void setName(String name) {
         nSetName(mNativeProxy, name);
     }
 
-    @Override
+    /**
+     * Blocks until all previously queued work has completed.
+     */
     void fence() {
         nFence(mNativeProxy);
     }
 
-    @Override
+    /**
+     * Prevents any further drawing until draw() is called. This is a signal
+     * that the contents of the RenderNode tree are no longer safe to play back.
+     * In practice this usually means that there are Functor pointers in the
+     * display list that are no longer valid.
+     */
     void stopDrawing() {
         nStopDrawing(mNativeProxy);
     }
 
-    @Override
+    /**
+     * Called by {@link ViewRootImpl} when a new performTraverals is scheduled.
+     */
     public void notifyFramePending() {
         nNotifyFramePending(mNativeProxy);
     }
 
-    @Override
+
     void registerAnimatingRenderNode(RenderNode animator) {
         nRegisterAnimatingRenderNode(mRootNode.mNativeRenderNode, animator.mNativeRenderNode);
     }
@@ -475,21 +887,6 @@
         }
     }
 
-    static void trimMemory(int level) {
-        nTrimMemory(level);
-    }
-
-    public static void overrideProperty(@NonNull String name, @NonNull String value) {
-        if (name == null || value == null) {
-            throw new IllegalArgumentException("name and value must be non-null");
-        }
-        nOverrideProperty(name, value);
-    }
-
-    public static void dumpProfileData(byte[] data, FileDescriptor fd) {
-        nDumpProfileData(data, fd);
-    }
-
     private static class ProcessInitializer {
         static ProcessInitializer sInstance = new ProcessInitializer();
         private static IBinder sProcToken;
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index da66f97..1b8ef9e 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -13270,7 +13270,7 @@
     /**
      * @hide
      */
-    public HardwareRenderer getHardwareRenderer() {
+    public ThreadedRenderer getHardwareRenderer() {
         return mAttachInfo != null ? mAttachInfo.mHardwareRenderer : null;
     }
 
@@ -17436,7 +17436,7 @@
         if (hasWindowFocus()) viewStateIndex |= StateSet.VIEW_STATE_WINDOW_FOCUSED;
         if ((privateFlags & PFLAG_ACTIVATED) != 0) viewStateIndex |= StateSet.VIEW_STATE_ACTIVATED;
         if (mAttachInfo != null && mAttachInfo.mHardwareAccelerationRequested &&
-                HardwareRenderer.isAvailable()) {
+                ThreadedRenderer.isAvailable()) {
             // This is set if HW acceleration is requested, even if the current
             // process doesn't allow it.  This is just to allow app preview
             // windows to better match their app.
@@ -22054,7 +22054,7 @@
 
         boolean mHardwareAccelerated;
         boolean mHardwareAccelerationRequested;
-        HardwareRenderer mHardwareRenderer;
+        ThreadedRenderer mHardwareRenderer;
         List<RenderNode> mPendingAnimatingRenderNodes;
 
         /**
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 3c9310d..b7bb9a3 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -102,7 +102,7 @@
  */
 @SuppressWarnings({"EmptyCatchBlock", "PointlessBooleanExpression"})
 public final class ViewRootImpl implements ViewParent,
-        View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks {
+        View.AttachInfo.Callbacks, ThreadedRenderer.HardwareDrawCallbacks {
     private static final String TAG = "ViewRootImpl";
     private static final boolean DBG = false;
     private static final boolean LOCAL_LOGV = false;
@@ -761,7 +761,7 @@
                 (attrs.flags & WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED) != 0;
 
         if (hardwareAccelerated) {
-            if (!HardwareRenderer.isAvailable()) {
+            if (!ThreadedRenderer.isAvailable()) {
                 return;
             }
 
@@ -784,8 +784,8 @@
                 // shows for launching applications, so they will look more like
                 // the app being launched.
                 mAttachInfo.mHardwareAccelerationRequested = true;
-            } else if (!HardwareRenderer.sRendererDisabled
-                    || (HardwareRenderer.sSystemRendererDisabled && forceHwAccelerated)) {
+            } else if (!ThreadedRenderer.sRendererDisabled
+                    || (ThreadedRenderer.sSystemRendererDisabled && forceHwAccelerated)) {
                 if (mAttachInfo.mHardwareRenderer != null) {
                     mAttachInfo.mHardwareRenderer.destroy();
                 }
@@ -794,7 +794,7 @@
                 final boolean hasSurfaceInsets = insets.left != 0 || insets.right != 0
                         || insets.top != 0 || insets.bottom != 0;
                 final boolean translucent = attrs.format != PixelFormat.OPAQUE || hasSurfaceInsets;
-                mAttachInfo.mHardwareRenderer = HardwareRenderer.create(mContext, translucent);
+                mAttachInfo.mHardwareRenderer = ThreadedRenderer.create(mContext, translucent);
                 if (mAttachInfo.mHardwareRenderer != null) {
                     mAttachInfo.mHardwareRenderer.setName(attrs.getTitle().toString());
                     mAttachInfo.mHardwareAccelerated =
@@ -1865,7 +1865,7 @@
                 }
             }
 
-            final HardwareRenderer hardwareRenderer = mAttachInfo.mHardwareRenderer;
+            final ThreadedRenderer hardwareRenderer = mAttachInfo.mHardwareRenderer;
             if (hardwareRenderer != null && hardwareRenderer.isEnabled()) {
                 if (hwInitialized
                         || mWidth != hardwareRenderer.getWidth()
@@ -5733,7 +5733,7 @@
     }
 
     private void destroyHardwareRenderer() {
-        HardwareRenderer hardwareRenderer = mAttachInfo.mHardwareRenderer;
+        ThreadedRenderer hardwareRenderer = mAttachInfo.mHardwareRenderer;
 
         if (hardwareRenderer != null) {
             if (mView != null) {
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index c08e1b5..8c68e92 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -425,7 +425,7 @@
                 mDyingViews.remove(view);
             }
         }
-        if (HardwareRenderer.sTrimForeground && HardwareRenderer.isAvailable()) {
+        if (ThreadedRenderer.sTrimForeground && ThreadedRenderer.isAvailable()) {
             doTrimForeground();
         }
     }
@@ -452,7 +452,7 @@
     }
 
     public void trimMemory(int level) {
-        if (HardwareRenderer.isAvailable()) {
+        if (ThreadedRenderer.isAvailable()) {
             if (shouldDestroyEglContext(level)) {
                 // Destroy all hardware surfaces and resources associated to
                 // known windows
@@ -465,16 +465,16 @@
                 level = ComponentCallbacks2.TRIM_MEMORY_COMPLETE;
             }
 
-            HardwareRenderer.trimMemory(level);
+            ThreadedRenderer.trimMemory(level);
 
-            if (HardwareRenderer.sTrimForeground) {
+            if (ThreadedRenderer.sTrimForeground) {
                 doTrimForeground();
             }
         }
     }
 
     public static void trimForeground() {
-        if (HardwareRenderer.sTrimForeground && HardwareRenderer.isAvailable()) {
+        if (ThreadedRenderer.sTrimForeground && ThreadedRenderer.isAvailable()) {
             WindowManagerGlobal wm = WindowManagerGlobal.getInstance();
             wm.doTrimForeground();
         }
@@ -494,7 +494,7 @@
             }
         }
         if (!hasVisibleWindows) {
-            HardwareRenderer.trimMemory(
+            ThreadedRenderer.trimMemory(
                     ComponentCallbacks2.TRIM_MEMORY_COMPLETE);
         }
     }
@@ -513,7 +513,7 @@
                     String name = getWindowName(root);
                     pw.printf("\n\t%s (visibility=%d)", name, root.getHostVisibility());
 
-                    HardwareRenderer renderer =
+                    ThreadedRenderer renderer =
                             root.getView().mAttachInfo.mHardwareRenderer;
                     if (renderer != null) {
                         renderer.dumpGfxInfo(pw, fd, args);
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index c3d0993..5146bc6 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -17,6 +17,7 @@
 package android.widget;
 
 import android.R;
+import android.annotation.IntDef;
 import android.annotation.Nullable;
 import android.app.PendingIntent;
 import android.app.PendingIntent.CanceledException;
@@ -106,6 +107,8 @@
 import com.android.internal.util.Preconditions;
 import com.android.internal.widget.EditableInputConnection;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.text.BreakIterator;
 import java.util.Arrays;
 import java.util.Comparator;
@@ -4087,7 +4090,17 @@
         }
     }
 
-    private class SelectionStartHandleView extends HandleView {
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({HANDLE_TYPE_SELECTION_START, HANDLE_TYPE_SELECTION_END})
+    public @interface HandleType {}
+    public static final int HANDLE_TYPE_SELECTION_START = 0;
+    public static final int HANDLE_TYPE_SELECTION_END = 1;
+
+    private class SelectionHandleView extends HandleView {
+        // Indicates the handle type, selection start (HANDLE_TYPE_SELECTION_START) or selection
+        // end (HANDLE_TYPE_SELECTION_END).
+        @HandleType
+        private final int mHandleType;
         // Indicates whether the cursor is making adjustments within a word.
         private boolean mInWord = false;
         // Difference between touch position and word boundary position.
@@ -4102,16 +4115,21 @@
         // Used to save text view location.
         private final int[] mTextViewLocation = new int[2];
 
-        public SelectionStartHandleView(Drawable drawableLtr, Drawable drawableRtl) {
-            super(drawableLtr, drawableRtl, com.android.internal.R.id.selection_start_handle);
-            ViewConfiguration viewConfiguration = ViewConfiguration.get(
-                    mTextView.getContext());
+        public SelectionHandleView(Drawable drawableLtr, Drawable drawableRtl, int id,
+                @HandleType int handleType) {
+            super(drawableLtr, drawableRtl, id);
+            mHandleType = handleType;
+            ViewConfiguration viewConfiguration = ViewConfiguration.get(mTextView.getContext());
             mTextViewEdgeSlop = viewConfiguration.getScaledTouchSlop() * 4;
         }
 
+        private boolean isStartHandle() {
+            return mHandleType == HANDLE_TYPE_SELECTION_START;
+        }
+
         @Override
         protected int getHotspotX(Drawable drawable, boolean isRtlRun) {
-            if (isRtlRun) {
+            if (isRtlRun == isStartHandle()) {
                 return drawable.getIntrinsicWidth() / 4;
             } else {
                 return (drawable.getIntrinsicWidth() * 3) / 4;
@@ -4120,18 +4138,23 @@
 
         @Override
         protected int getHorizontalGravity(boolean isRtlRun) {
-            return isRtlRun ? Gravity.LEFT : Gravity.RIGHT;
+            return (isRtlRun == isStartHandle()) ? Gravity.LEFT : Gravity.RIGHT;
         }
 
         @Override
         public int getCurrentCursorOffset() {
-            return mTextView.getSelectionStart();
+            return isStartHandle() ? mTextView.getSelectionStart() : mTextView.getSelectionEnd();
         }
 
         @Override
-        public void updateSelection(int offset) {
-            Selection.setSelection((Spannable) mTextView.getText(), offset,
-                    mTextView.getSelectionEnd());
+        protected void updateSelection(int offset) {
+            if (isStartHandle()) {
+                Selection.setSelection((Spannable) mTextView.getText(), offset,
+                        mTextView.getSelectionEnd());
+            } else {
+                Selection.setSelection((Spannable) mTextView.getText(),
+                        mTextView.getSelectionStart(), offset);
+            }
             updateDrawable();
             if (mTextActionMode != null) {
                 mTextActionMode.invalidate();
@@ -4153,35 +4176,36 @@
             }
 
             boolean positionCursor = false;
-            final int selectionEnd = mTextView.getSelectionEnd();
+            final int anotherHandleOffset =
+                    isStartHandle() ? mTextView.getSelectionEnd() : mTextView.getSelectionStart();
             int currLine = getCurrentLineAdjustedForSlop(layout, mPreviousLineTouched, y);
             int initialOffset = mTextView.getOffsetAtCoordinate(currLine, x);
 
-            if (initialOffset >= selectionEnd) {
-                // Handles have crossed, bound it to the last selected line and
+            if (isStartHandle() && initialOffset >= anotherHandleOffset
+                    || !isStartHandle() && initialOffset <= anotherHandleOffset) {
+                // Handles have crossed, bound it to the first selected line and
                 // adjust by word / char as normal.
-                currLine = layout.getLineForOffset(selectionEnd);
+                currLine = layout.getLineForOffset(anotherHandleOffset);
                 initialOffset = mTextView.getOffsetAtCoordinate(currLine, x);
             }
 
             int offset = initialOffset;
-            int end = getWordEnd(offset);
-            int start = getWordStart(offset);
+            final int wordEnd = getWordEnd(offset);
+            final int wordStart = getWordStart(offset);
 
             if (mPrevX == UNSET_X_VALUE) {
                 mPrevX = x;
             }
 
-            final int selectionStart = mTextView.getSelectionStart();
-            final boolean selectionStartRtl = layout.isRtlCharAt(selectionStart);
+            final int currentOffset = getCurrentCursorOffset();
+            final boolean rtlAtCurrentOffset = layout.isRtlCharAt(currentOffset);
             final boolean atRtl = layout.isRtlCharAt(offset);
             final boolean isLvlBoundary = layout.isLevelBoundary(offset);
-            boolean isExpanding;
 
             // We can't determine if the user is expanding or shrinking the selection if they're
             // on a bi-di boundary, so until they've moved past the boundary we'll just place
             // the cursor at the current position.
-            if (isLvlBoundary || (selectionStartRtl && !atRtl) || (!selectionStartRtl && atRtl)) {
+            if (isLvlBoundary || (rtlAtCurrentOffset && !atRtl) || (!rtlAtCurrentOffset && atRtl)) {
                 // We're on a boundary or this is the first direction change -- just update
                 // to the current position.
                 mLanguageDirectionChanged = true;
@@ -4195,24 +4219,30 @@
                 mTouchWordDelta = 0.0f;
                 mLanguageDirectionChanged = false;
                 return;
+            }
+
+            boolean isExpanding;
+            final float xDiff = x - mPrevX;
+            if (atRtl == isStartHandle()) {
+                isExpanding = xDiff > 0 || currLine > mPreviousLineTouched;
             } else {
-                final float xDiff = x - mPrevX;
-                if (atRtl) {
-                    isExpanding = xDiff > 0 || currLine > mPreviousLineTouched;
-                } else {
-                    isExpanding = xDiff < 0 || currLine < mPreviousLineTouched;
-                }
+                isExpanding = xDiff < 0 || currLine < mPreviousLineTouched;
             }
 
             if (mTextView.getHorizontallyScrolling()) {
                 if (positionNearEdgeOfScrollingView(x, atRtl)
-                        && (mTextView.getScrollX() != 0)
-                        && ((isExpanding && offset < selectionStart) || !isExpanding)) {
-                    // If we're expanding ensure that the offset is smaller than the
-                    // selection start, if the handle snapped to the word, the finger position
+                        && ((isStartHandle() && mTextView.getScrollX() != 0)
+                                || (!isStartHandle()
+                                        && mTextView.canScrollHorizontally(atRtl ? -1 : 1)))
+                        && ((isExpanding && ((isStartHandle() && offset < currentOffset)
+                                || (!isStartHandle() && offset > currentOffset)))
+                                        || !isExpanding)) {
+                    // If we're expanding ensure that the offset is actually expanding compared to
+                    // the current offset, if the handle snapped to the word, the finger position
                     // may be out of sync and we don't want the selection to jump back.
                     mTouchWordDelta = 0.0f;
-                    final int nextOffset = atRtl ? layout.getOffsetToRightOf(mPreviousOffset)
+                    final int nextOffset = (atRtl == isStartHandle())
+                            ? layout.getOffsetToRightOf(mPreviousOffset)
                             : layout.getOffsetToLeftOf(mPreviousOffset);
                     positionAndAdjustForCrossingHandles(nextOffset);
                     return;
@@ -4221,24 +4251,36 @@
 
             if (isExpanding) {
                 // User is increasing the selection.
-                if (!mInWord || currLine < mPrevLine) {
+                final boolean snapToWord = !mInWord
+                        || (isStartHandle() ? currLine < mPrevLine : currLine > mPrevLine);
+                if (snapToWord) {
                     // Sometimes words can be broken across lines (Chinese, hyphenation).
-                    // We still snap to the start of the word but we only use the letters on the
+                    // We still snap to the word boundary but we only use the letters on the
                     // current line to determine if the user is far enough into the word to snap.
-                    int wordStartOnCurrLine = start;
-                    if (layout != null && layout.getLineForOffset(start) != currLine) {
-                        wordStartOnCurrLine = layout.getLineStart(currLine);
+                    int wordBoundary = isStartHandle() ? wordStart : wordEnd;
+                    if (layout != null && layout.getLineForOffset(wordBoundary) != currLine) {
+                        wordBoundary = isStartHandle() ?
+                                layout.getLineStart(currLine) : layout.getLineEnd(currLine);
                     }
-                    int offsetThresholdToSnap = end - ((end - wordStartOnCurrLine) / 2);
-                    if (offset <= offsetThresholdToSnap || currLine < mPrevLine) {
-                        // User is far enough into the word or on a different
-                        // line so we expand by word.
-                        offset = start;
+                    final int offsetThresholdToSnap = isStartHandle()
+                            ? wordEnd - ((wordEnd - wordBoundary) / 2)
+                            : wordStart + ((wordBoundary - wordStart) / 2);
+                    if (isStartHandle()
+                            && (offset <= offsetThresholdToSnap || currLine < mPrevLine)) {
+                        // User is far enough into the word or on a different line so we expand by
+                        // word.
+                        offset = wordStart;
+                    } else if (!isStartHandle()
+                            && (offset >= offsetThresholdToSnap || currLine > mPrevLine)) {
+                        // User is far enough into the word or on a different line so we expand by
+                        // word.
+                        offset = wordEnd;
                     } else {
                         offset = mPreviousOffset;
                     }
                 }
-                if (layout != null && offset < initialOffset) {
+                if (layout != null && (isStartHandle() && offset < initialOffset)
+                        || (!isStartHandle() && offset > initialOffset)) {
                     final float adjustedX = layout.getPrimaryHorizontal(offset);
                     mTouchWordDelta =
                             mTextView.convertToLocalHorizontalCoordinate(x) - adjustedX;
@@ -4249,12 +4291,16 @@
             } else {
                 final int adjustedOffset =
                         mTextView.getOffsetAtCoordinate(currLine, x - mTouchWordDelta);
-                if (adjustedOffset > mPreviousOffset || currLine > mPrevLine) {
+                final boolean shrinking = isStartHandle()
+                        ? adjustedOffset > mPreviousOffset || currLine > mPrevLine
+                        : adjustedOffset < mPreviousOffset || currLine < mPrevLine;
+                if (shrinking) {
                     // User is shrinking the selection.
-                    if (currLine > mPrevLine) {
+                    if (currLine != mPrevLine) {
                         // We're on a different line, so we'll snap to word boundaries.
-                        offset = start;
-                        if (layout != null && offset < initialOffset) {
+                        offset = isStartHandle() ? wordStart : wordEnd;
+                        if (layout != null && (isStartHandle() && offset < initialOffset)
+                                || (!isStartHandle() && offset > initialOffset)) {
                             final float adjustedX = layout.getPrimaryHorizontal(offset);
                             mTouchWordDelta =
                                     mTextView.convertToLocalHorizontalCoordinate(x) - adjustedX;
@@ -4265,11 +4311,12 @@
                         offset = adjustedOffset;
                     }
                     positionCursor = true;
-                } else if (adjustedOffset < mPreviousOffset) {
-                    // Handle has jumped to the start of the word, and the user is moving
+                } else if ((isStartHandle() && adjustedOffset < mPreviousOffset)
+                        || (!isStartHandle() && adjustedOffset > mPreviousOffset)) {
+                    // Handle has jumped to the word boundary, and the user is moving
                     // their finger towards the handle, the delta should be updated.
-                    mTouchWordDelta = mTextView.convertToLocalHorizontalCoordinate(x)
-                            - layout.getPrimaryHorizontal(mPreviousOffset);
+                    mTouchWordDelta = mTextView.convertToLocalHorizontalCoordinate(x) -
+                            layout.getPrimaryHorizontal(mPreviousOffset);
                 }
             }
 
@@ -4280,16 +4327,6 @@
             mPrevX = x;
         }
 
-        private void positionAndAdjustForCrossingHandles(int offset) {
-            final int selectionEnd = mTextView.getSelectionEnd();
-            if (offset >= selectionEnd) {
-                // Handles can not cross and selection is at least one character.
-                offset = getNextCursorOffset(selectionEnd, false);
-                mTouchWordDelta = 0.0f;
-            }
-            positionAtCursorOffset(offset, false);
-        }
-
         /**
          * @param offset Cursor offset. Must be in [-1, length].
          * @param parentScrolled If the parent has been scrolled or not.
@@ -4312,256 +4349,28 @@
             return superResult;
         }
 
-        private boolean positionNearEdgeOfScrollingView(float x, boolean atRtl) {
-            mTextView.getLocationOnScreen(mTextViewLocation);
-            boolean nearEdge;
-            if (atRtl) {
-                int rightEdge = mTextViewLocation[0] + mTextView.getWidth()
-                        - mTextView.getPaddingRight();
-                nearEdge = x > rightEdge - mTextViewEdgeSlop;
-            } else {
-                int leftEdge = mTextViewLocation[0] + mTextView.getPaddingLeft();
-                nearEdge = x < leftEdge + mTextViewEdgeSlop;
-            }
-            return nearEdge;
-        }
-    }
-
-    private class SelectionEndHandleView extends HandleView {
-        // Indicates whether the cursor is making adjustments within a word.
-        private boolean mInWord = false;
-        // Difference between touch position and word boundary position.
-        private float mTouchWordDelta;
-        // X value of the previous updatePosition call.
-        private float mPrevX;
-        // Indicates if the handle has moved a boundary between LTR and RTL text.
-        private boolean mLanguageDirectionChanged = false;
-        // Distance from edge of horizontally scrolling text view
-        // to use to switch to character mode.
-        private final float mTextViewEdgeSlop;
-        // Used to save the text view location.
-        private final int[] mTextViewLocation = new int[2];
-
-        public SelectionEndHandleView(Drawable drawableLtr, Drawable drawableRtl) {
-            super(drawableLtr, drawableRtl, com.android.internal.R.id.selection_end_handle);
-            ViewConfiguration viewConfiguration = ViewConfiguration.get(
-                    mTextView.getContext());
-            mTextViewEdgeSlop = viewConfiguration.getScaledTouchSlop() * 4;
-        }
-
-        @Override
-        protected int getHotspotX(Drawable drawable, boolean isRtlRun) {
-            if (isRtlRun) {
-                return (drawable.getIntrinsicWidth() * 3) / 4;
-            } else {
-                return drawable.getIntrinsicWidth() / 4;
-            }
-        }
-
-        @Override
-        protected int getHorizontalGravity(boolean isRtlRun) {
-            return isRtlRun ? Gravity.RIGHT : Gravity.LEFT;
-        }
-
-        @Override
-        public int getCurrentCursorOffset() {
-            return mTextView.getSelectionEnd();
-        }
-
-        @Override
-        public void updateSelection(int offset) {
-            Selection.setSelection((Spannable) mTextView.getText(),
-                    mTextView.getSelectionStart(), offset);
-            if (mTextActionMode != null) {
-                mTextActionMode.invalidate();
-            }
-            updateDrawable();
-        }
-
-        @Override
-        public void updatePosition(float x, float y) {
-            final Layout layout = mTextView.getLayout();
-            if (layout == null) {
-                // HandleView will deal appropriately in positionAtCursorOffset when
-                // layout is null.
-                positionAndAdjustForCrossingHandles(mTextView.getOffsetForPosition(x, y));
-                return;
-            }
-
-            if (mPreviousLineTouched == UNSET_LINE) {
-                mPreviousLineTouched = mTextView.getLineAtCoordinate(y);
-            }
-
-            boolean positionCursor = false;
-            final int selectionStart = mTextView.getSelectionStart();
-            int currLine = getCurrentLineAdjustedForSlop(layout, mPreviousLineTouched, y);
-            int initialOffset = mTextView.getOffsetAtCoordinate(currLine, x);
-
-            if (initialOffset <= selectionStart) {
-                // Handles have crossed, bound it to the first selected line and
-                // adjust by word / char as normal.
-                currLine = layout.getLineForOffset(selectionStart);
-                initialOffset = mTextView.getOffsetAtCoordinate(currLine, x);
-            }
-
-            int offset = initialOffset;
-            int end = getWordEnd(offset);
-            int start = getWordStart(offset);
-
-            if (mPrevX == UNSET_X_VALUE) {
-                mPrevX = x;
-            }
-
-            final int selectionEnd = mTextView.getSelectionEnd();
-            final boolean selectionEndRtl = layout.isRtlCharAt(selectionEnd);
-            final boolean atRtl = layout.isRtlCharAt(offset);
-            final boolean isLvlBoundary = layout.isLevelBoundary(offset);
-            boolean isExpanding;
-
-            // We can't determine if the user is expanding or shrinking the selection if they're
-            // on a bi-di boundary, so until they've moved past the boundary we'll just place
-            // the cursor at the current position.
-            if (isLvlBoundary || (selectionEndRtl && !atRtl) || (!selectionEndRtl && atRtl)) {
-                // We're on a boundary or this is the first direction change -- just update
-                // to the current position.
-                mLanguageDirectionChanged = true;
-                mTouchWordDelta = 0.0f;
-                positionAndAdjustForCrossingHandles(offset);
-                return;
-            } else if (mLanguageDirectionChanged && !isLvlBoundary) {
-                // We've just moved past the boundary so update the position. After this we can
-                // figure out if the user is expanding or shrinking to go by word or character.
-                positionAndAdjustForCrossingHandles(offset);
-                mTouchWordDelta = 0.0f;
-                mLanguageDirectionChanged = false;
-                return;
-            } else {
-                final float xDiff = x - mPrevX;
-                if (atRtl) {
-                    isExpanding = xDiff < 0 || currLine < mPreviousLineTouched;
-                } else {
-                    isExpanding = xDiff > 0 || currLine > mPreviousLineTouched;
-                }
-            }
-
-            if (mTextView.getHorizontallyScrolling()) {
-                if (positionNearEdgeOfScrollingView(x, atRtl)
-                        && mTextView.canScrollHorizontally(atRtl ? -1 : 1)
-                        && ((isExpanding && offset > selectionEnd) || !isExpanding)) {
-                    // If we're expanding ensure that the offset is actually greater than the
-                    // selection end, if the handle snapped to the word, the finger position
-                    // may be out of sync and we don't want the selection to jump back.
-                    mTouchWordDelta = 0.0f;
-                    final int nextOffset = atRtl ? layout.getOffsetToLeftOf(mPreviousOffset)
-                            : layout.getOffsetToRightOf(mPreviousOffset);
-                    positionAndAdjustForCrossingHandles(nextOffset);
-                    return;
-                }
-            }
-
-            if (isExpanding) {
-                // User is increasing the selection.
-                if (!mInWord || currLine > mPrevLine) {
-                    // Sometimes words can be broken across lines (Chinese, hyphenation).
-                    // We still snap to the end of the word but we only use the letters on the
-                    // current line to determine if the user is far enough into the word to snap.
-                    int wordEndOnCurrLine = end;
-                    if (layout != null && layout.getLineForOffset(end) != currLine) {
-                        wordEndOnCurrLine = layout.getLineEnd(currLine);
-                    }
-                    final int offsetThresholdToSnap = start + ((wordEndOnCurrLine - start) / 2);
-                    if (offset >= offsetThresholdToSnap || currLine > mPrevLine) {
-                        // User is far enough into the word or on a different
-                        // line so we expand by word.
-                        offset = end;
-                    } else {
-                        offset = mPreviousOffset;
-                    }
-                }
-                if (offset > initialOffset) {
-                    final float adjustedX = layout.getPrimaryHorizontal(offset);
-                    mTouchWordDelta =
-                            adjustedX - mTextView.convertToLocalHorizontalCoordinate(x);
-                } else {
-                    mTouchWordDelta = 0.0f;
-                }
-                positionCursor = true;
-            } else {
-                final int adjustedOffset =
-                        mTextView.getOffsetAtCoordinate(currLine, x + mTouchWordDelta);
-                if (adjustedOffset < mPreviousOffset || currLine < mPrevLine) {
-                    // User is shrinking the selection.
-                    if (currLine < mPrevLine) {
-                        // We're on a different line, so we'll snap to word boundaries.
-                        offset = end;
-                        if (offset > initialOffset) {
-                            final float adjustedX = layout.getPrimaryHorizontal(offset);
-                            mTouchWordDelta =
-                                    adjustedX - mTextView.convertToLocalHorizontalCoordinate(x);
-                        } else {
-                            mTouchWordDelta = 0.0f;
-                        }
-                    } else {
-                        offset = adjustedOffset;
-                    }
-                    positionCursor = true;
-                } else if (adjustedOffset > mPreviousOffset) {
-                    // Handle has jumped to the end of the word, and the user is moving
-                    // their finger towards the handle, the delta should be updated.
-                    mTouchWordDelta = layout.getPrimaryHorizontal(mPreviousOffset)
-                            - mTextView.convertToLocalHorizontalCoordinate(x);
-                }
-            }
-
-            if (positionCursor) {
-                mPreviousLineTouched = currLine;
-                positionAndAdjustForCrossingHandles(offset);
-            }
-            mPrevX = x;
-        }
-
         private void positionAndAdjustForCrossingHandles(int offset) {
-            final int selectionStart = mTextView.getSelectionStart();
-            if (offset <= selectionStart) {
+            final int anotherHandleOffset =
+                    isStartHandle() ? mTextView.getSelectionEnd() : mTextView.getSelectionStart();
+            if ((isStartHandle() && offset >= anotherHandleOffset)
+                    || (!isStartHandle() && offset <= anotherHandleOffset)) {
                 // Handles can not cross and selection is at least one character.
-                offset = getNextCursorOffset(selectionStart, true);
+                offset = getNextCursorOffset(anotherHandleOffset, !isStartHandle());
                 mTouchWordDelta = 0.0f;
             }
             positionAtCursorOffset(offset, false);
         }
 
-        /**
-         * @param offset Cursor offset. Must be in [-1, length].
-         * @param parentScrolled If the parent has been scrolled or not.
-         */
-        @Override
-        protected void positionAtCursorOffset(int offset, boolean parentScrolled) {
-            super.positionAtCursorOffset(offset, parentScrolled);
-            mInWord = (offset != -1) && !getWordIteratorWithText().isBoundary(offset);
-        }
-
-        @Override
-        public boolean onTouchEvent(MotionEvent event) {
-            boolean superResult = super.onTouchEvent(event);
-            if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
-                // Reset the touch word offset and x value when the user
-                // re-engages the handle.
-                mTouchWordDelta = 0.0f;
-                mPrevX = UNSET_X_VALUE;
-            }
-            return superResult;
-        }
-
         private boolean positionNearEdgeOfScrollingView(float x, boolean atRtl) {
             mTextView.getLocationOnScreen(mTextViewLocation);
             boolean nearEdge;
-            if (atRtl) {
-                int leftEdge = mTextViewLocation[0] + mTextView.getPaddingLeft();
-                nearEdge = x < leftEdge + mTextViewEdgeSlop;
-            } else {
+            if (atRtl == isStartHandle()) {
                 int rightEdge = mTextViewLocation[0] + mTextView.getWidth()
                         - mTextView.getPaddingRight();
                 nearEdge = x > rightEdge - mTextViewEdgeSlop;
+            } else {
+                int leftEdge = mTextViewLocation[0] + mTextView.getPaddingLeft();
+                nearEdge = x < leftEdge + mTextViewEdgeSlop;
             }
             return nearEdge;
         }
@@ -4673,8 +4482,8 @@
 
     class SelectionModifierCursorController implements CursorController {
         // The cursor controller handles, lazily created when shown.
-        private SelectionStartHandleView mStartHandle;
-        private SelectionEndHandleView mEndHandle;
+        private SelectionHandleView mStartHandle;
+        private SelectionHandleView mEndHandle;
         // The offsets of that last touch down event. Remembered to start selection there.
         private int mMinTouchOffset, mMaxTouchOffset;
 
@@ -4718,10 +4527,14 @@
         private void initHandles() {
             // Lazy object creation has to be done before updatePosition() is called.
             if (mStartHandle == null) {
-                mStartHandle = new SelectionStartHandleView(mSelectHandleLeft, mSelectHandleRight);
+                mStartHandle = new SelectionHandleView(mSelectHandleLeft, mSelectHandleRight,
+                        com.android.internal.R.id.selection_start_handle,
+                        HANDLE_TYPE_SELECTION_START);
             }
             if (mEndHandle == null) {
-                mEndHandle = new SelectionEndHandleView(mSelectHandleRight, mSelectHandleLeft);
+                mEndHandle = new SelectionHandleView(mSelectHandleRight, mSelectHandleLeft,
+                        com.android.internal.R.id.selection_end_handle,
+                        HANDLE_TYPE_SELECTION_END);
             }
 
             mStartHandle.show();
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 0ed72e4d..0dd803a2 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -1666,6 +1666,7 @@
             ViewGroup.LayoutParams layoutParams = target.getLayoutParams();
             if (layoutParams instanceof ViewGroup.MarginLayoutParams) {
                 ((ViewGroup.MarginLayoutParams) layoutParams).setMarginEnd(end);
+                target.setLayoutParams(layoutParams);
             }
         }
 
diff --git a/core/java/com/android/internal/logging/MetricsLogger.java b/core/java/com/android/internal/logging/MetricsLogger.java
index c992c70..cc677cc 100644
--- a/core/java/com/android/internal/logging/MetricsLogger.java
+++ b/core/java/com/android/internal/logging/MetricsLogger.java
@@ -34,6 +34,7 @@
     public static final int ACTION_ZEN_ALLOW_PEEK = 261;
     public static final int ACTION_ZEN_ALLOW_LIGHTS = 262;
     public static final int NOTIFICATION_TOPIC_NOTIFICATION = 263;
+    public static final int ACTION_DEFAULT_SMS_APP_CHANGED = 264;
 
     public static void visible(Context context, int category) throws IllegalArgumentException {
         if (Build.IS_DEBUGGABLE && category == VIEW_UNKNOWN) {
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 9391c60..4a969b2 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -105,7 +105,7 @@
     private static final int MAGIC = 0xBA757475; // 'BATSTATS'
 
     // Current on-disk Parcel version
-    private static final int VERSION = 136 + (USE_OLD_HISTORY ? 1000 : 0);
+    private static final int VERSION = 138 + (USE_OLD_HISTORY ? 1000 : 0);
 
     // Maximum number of items we will record in the history.
     private static final int MAX_HISTORY_ITEMS = 2000;
@@ -2621,10 +2621,9 @@
         }
     }
 
-    public void noteProcessStateLocked(String name, int uid, int state) {
+    public void noteUidProcessStateLocked(int uid, int state) {
         uid = mapUid(uid);
-        final long elapsedRealtime = SystemClock.elapsedRealtime();
-        getUidStatsLocked(uid).updateProcessStateLocked(name, state, elapsedRealtime);
+        getUidStatsLocked(uid).updateUidProcessStateLocked(state);
     }
 
     public void noteProcessFinishLocked(String name, int uid) {
@@ -2632,13 +2631,11 @@
         if (!mActiveEvents.updateState(HistoryItem.EVENT_PROC_FINISH, name, uid, 0)) {
             return;
         }
-        final long elapsedRealtime = SystemClock.elapsedRealtime();
-        final long uptime = SystemClock.uptimeMillis();
-        getUidStatsLocked(uid).updateProcessStateLocked(name, Uid.PROCESS_STATE_NONE,
-                elapsedRealtime);
         if (!mRecordAllHistory) {
             return;
         }
+        final long elapsedRealtime = SystemClock.elapsedRealtime();
+        final long uptime = SystemClock.uptimeMillis();
         addHistoryEventLocked(elapsedRealtime, uptime, HistoryItem.EVENT_PROC_FINISH, name, uid);
     }
 
@@ -4446,8 +4443,7 @@
 
         StopwatchTimer mForegroundActivityTimer;
 
-        static final int PROCESS_STATE_NONE = NUM_PROCESS_STATE;
-        int mProcessState = PROCESS_STATE_NONE;
+        int mProcessState = ActivityManager.PROCESS_STATE_NONEXISTENT;
         StopwatchTimer[] mProcessStateTimer;
 
         BatchTimer mVibratorOnTimer;
@@ -4812,21 +4808,6 @@
             }
         }
 
-        void updateUidProcessStateLocked(int state, long elapsedRealtimeMs) {
-            if (mProcessState == state) return;
-
-            if (mProcessState != PROCESS_STATE_NONE) {
-                mProcessStateTimer[mProcessState].stopRunningLocked(elapsedRealtimeMs);
-            }
-            mProcessState = state;
-            if (state != PROCESS_STATE_NONE) {
-                if (mProcessStateTimer[state] == null) {
-                    makeProcessState(state, null);
-                }
-                mProcessStateTimer[state].startRunningLocked(elapsedRealtimeMs);
-            }
-        }
-
         public BatchTimer createVibratorOnTimerLocked() {
             if (mVibratorOnTimer == null) {
                 mVibratorOnTimer = new BatchTimer(Uid.this, VIBRATOR_ON, mOnBatteryTimeBase);
@@ -5167,7 +5148,7 @@
                         active |= !mProcessStateTimer[i].reset(false);
                     }
                 }
-                active |= (mProcessState != PROCESS_STATE_NONE);
+                active |= (mProcessState != ActivityManager.PROCESS_STATE_NONEXISTENT);
             }
             if (mVibratorOnTimer != null) {
                 if (mVibratorOnTimer.reset(false)) {
@@ -5261,14 +5242,9 @@
             }
             for (int ip=mProcessStats.size()-1; ip>=0; ip--) {
                 Proc proc = mProcessStats.valueAt(ip);
-                if (proc.mProcessState == PROCESS_STATE_NONE) {
-                    proc.detach();
-                    mProcessStats.removeAt(ip);
-                } else {
-                    proc.reset();
-                    active = true;
-                }
+                proc.detach();
             }
+            mProcessStats.clear();
             if (mPids.size() > 0) {
                 for (int i=mPids.size()-1; i>=0; i--) {
                     Pid pid = mPids.valueAt(i);
@@ -5697,7 +5673,7 @@
             } else {
                 mForegroundActivityTimer = null;
             }
-            mProcessState = PROCESS_STATE_NONE;
+            mProcessState = ActivityManager.PROCESS_STATE_NONEXISTENT;
             for (int i = 0; i < NUM_PROCESS_STATE; i++) {
                 if (in.readInt() != 0) {
                     makeProcessState(i, in);
@@ -6080,11 +6056,6 @@
              */
             int mUnpluggedNumAnrs;
 
-            /**
-             * Current process state.
-             */
-            int mProcessState = PROCESS_STATE_NONE;
-
             ArrayList<ExcessivePower> mExcessivePower;
 
             Proc(String name) {
@@ -6104,16 +6075,6 @@
             public void onTimeStopped(long elapsedRealtime, long baseUptime, long baseRealtime) {
             }
 
-            void reset() {
-                mUserTime = mSystemTime = mForegroundTime = 0;
-                mStarts = mNumCrashes = mNumAnrs = 0;
-                mLoadedUserTime = mLoadedSystemTime = mLoadedForegroundTime = 0;
-                mLoadedStarts = mLoadedNumCrashes = mLoadedNumAnrs = 0;
-                mUnpluggedUserTime = mUnpluggedSystemTime = mUnpluggedForegroundTime = 0;
-                mUnpluggedStarts = mUnpluggedNumCrashes = mUnpluggedNumAnrs = 0;
-                mExcessivePower = null;
-            }
-
             void detach() {
                 mActive = false;
                 mOnBatteryTimeBase.remove(this);
@@ -6668,46 +6629,39 @@
             return ps;
         }
 
-        public void updateProcessStateLocked(String procName, int state, long elapsedRealtimeMs) {
-            int procState;
-            if (state <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) {
-                procState = PROCESS_STATE_FOREGROUND;
-            } else if (state <= ActivityManager.PROCESS_STATE_RECEIVER) {
-                procState = PROCESS_STATE_ACTIVE;
+        public void updateUidProcessStateLocked(int procState) {
+            int uidRunningState;
+            if (procState == ActivityManager.PROCESS_STATE_NONEXISTENT) {
+                uidRunningState = ActivityManager.PROCESS_STATE_NONEXISTENT;
+            } else if (procState == ActivityManager.PROCESS_STATE_TOP) {
+                uidRunningState = PROCESS_STATE_TOP;
+            } else if (procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {
+                // Persistent and other foreground states go here.
+                uidRunningState = PROCESS_STATE_FOREGROUND_SERVICE;
+            } else if (procState <= ActivityManager.PROCESS_STATE_TOP_SLEEPING) {
+                uidRunningState = PROCESS_STATE_TOP_SLEEPING;
+            } else if (procState <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) {
+                // Persistent and other foreground states go here.
+                uidRunningState = PROCESS_STATE_FOREGROUND;
+            } else if (procState <= ActivityManager.PROCESS_STATE_RECEIVER) {
+                uidRunningState = PROCESS_STATE_BACKGROUND;
             } else {
-                procState = PROCESS_STATE_RUNNING;
+                uidRunningState = PROCESS_STATE_CACHED;
             }
-            updateRealProcessStateLocked(procName, procState, elapsedRealtimeMs);
-        }
 
-        public void updateRealProcessStateLocked(String procName, int procState,
-                long elapsedRealtimeMs) {
-            Proc proc = getProcessStatsLocked(procName);
-            if (proc.mProcessState != procState) {
-                boolean changed;
-                if (procState < proc.mProcessState) {
-                    // Has this process become more important?  If so,
-                    // we may need to change the uid if the currrent uid proc state
-                    // is not as important as what we are now setting.
-                    changed = mProcessState > procState;
-                } else {
-                    // Has this process become less important?  If so,
-                    // we may need to change the uid if the current uid proc state
-                    // is the same importance as the old setting.
-                    changed = mProcessState == proc.mProcessState;
+            if (mProcessState == uidRunningState) return;
+
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+
+            if (mProcessState != ActivityManager.PROCESS_STATE_NONEXISTENT) {
+                mProcessStateTimer[mProcessState].stopRunningLocked(elapsedRealtime);
+            }
+            mProcessState = uidRunningState;
+            if (uidRunningState != ActivityManager.PROCESS_STATE_NONEXISTENT) {
+                if (mProcessStateTimer[uidRunningState] == null) {
+                    makeProcessState(uidRunningState, null);
                 }
-                proc.mProcessState = procState;
-                if (changed) {
-                    // uid's state may have changed; compute what the new state should be.
-                    int uidProcState = PROCESS_STATE_NONE;
-                    for (int ip=mProcessStats.size()-1; ip>=0; ip--) {
-                        proc = mProcessStats.valueAt(ip);
-                        if (proc.mProcessState < uidProcState) {
-                            uidProcState = proc.mProcessState;
-                        }
-                    }
-                    updateUidProcessStateLocked(uidProcState, elapsedRealtimeMs);
-                }
+                mProcessStateTimer[uidRunningState].startRunningLocked(elapsedRealtime);
             }
         }
 
@@ -9423,7 +9377,7 @@
             if (in.readInt() != 0) {
                 u.createForegroundActivityTimerLocked().readSummaryFromParcelLocked(in);
             }
-            u.mProcessState = Uid.PROCESS_STATE_NONE;
+            u.mProcessState = ActivityManager.PROCESS_STATE_NONEXISTENT;
             for (int i = 0; i < Uid.NUM_PROCESS_STATE; i++) {
                 if (in.readInt() != 0) {
                     u.makeProcessState(i, null);
diff --git a/core/java/com/android/internal/widget/FloatingToolbar.java b/core/java/com/android/internal/widget/FloatingToolbar.java
index 7bab446..3a00469 100644
--- a/core/java/com/android/internal/widget/FloatingToolbar.java
+++ b/core/java/com/android/internal/widget/FloatingToolbar.java
@@ -20,6 +20,7 @@
 import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
 import android.content.ComponentCallbacks;
 import android.content.Context;
 import android.content.res.Configuration;
@@ -28,7 +29,9 @@
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.Region;
+import android.graphics.drawable.AnimatedVectorDrawable;
 import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
 import android.text.TextUtils;
 import android.util.Size;
 import android.view.ContextThemeWrapper;
@@ -36,6 +39,7 @@
 import android.view.LayoutInflater;
 import android.view.Menu;
 import android.view.MenuItem;
+import android.view.MotionEvent;
 import android.view.View;
 import android.view.View.MeasureSpec;
 import android.view.ViewGroup;
@@ -45,6 +49,8 @@
 import android.view.animation.Animation;
 import android.view.animation.AnimationSet;
 import android.view.animation.Transformation;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
 import android.widget.AdapterView;
 import android.widget.ArrayAdapter;
 import android.widget.Button;
@@ -277,70 +283,56 @@
      * A popup window used by the floating toolbar.
      *
      * This class is responsible for the rendering/animation of the floating toolbar.
-     * It can hold one of 2 panels (i.e. main panel and overflow panel) at a time.
-     * It delegates specific panel functionality to the appropriate panel.
+     * It holds 2 panels (i.e. main panel and overflow panel) and an overflow button
+     * to transition between panels.
      */
     private static final class FloatingToolbarPopup {
 
-        public static final int OVERFLOW_DIRECTION_UP = 0;
-        public static final int OVERFLOW_DIRECTION_DOWN = 1;
+        /* Minimum and maximum number of items allowed in the overflow. */
+        private static final int MIN_OVERFLOW_SIZE = 2;
+        private static final int MAX_OVERFLOW_SIZE = 4;
+
+        /* The duration of the overflow button vector animation duration. */
+        private static final int OVERFLOW_BUTTON_ANIMATION_DELAY = 400;
 
         private final Context mContext;
-        private final View mParent;
+        private final View mParent;  // Parent for the popup window.
         private final PopupWindow mPopupWindow;
-        private final ViewGroup mContentContainer;
+
+        /* Margins between the popup window and it's content. */
         private final int mMarginHorizontal;
         private final int mMarginVertical;
 
-        private final Animation.AnimationListener mOnOverflowOpened =
-                new Animation.AnimationListener() {
-                    @Override
-                    public void onAnimationStart(Animation animation) {}
+        /* View components */
+        private final ViewGroup mContentContainer;  // holds all contents.
+        private final ViewGroup mMainPanel;  // holds menu items that are initially displayed.
+        private final ListView mOverflowPanel;  // holds menu items hidden in the overflow.
+        private final ImageButton mOverflowButton;  // opens/closes the overflow.
+        /* overflow button drawables. */
+        private final Drawable mArrow;
+        private final Drawable mOverflow;
+        private final AnimatedVectorDrawable mToArrow;
+        private final AnimatedVectorDrawable mToOverflow;
 
-                    @Override
-                    public void onAnimationEnd(Animation animation) {
-                        setOverflowPanelAsContent();
-                        mOverflowPanel.fadeIn(true);
-                    }
+        private final OverflowPanelViewHelper mOverflowPanelViewHelper;
 
-                    @Override
-                    public void onAnimationRepeat(Animation animation) {}
-                };
-        private final Animation.AnimationListener mOnOverflowClosed =
-                new Animation.AnimationListener() {
-                    @Override
-                    public void onAnimationStart(Animation animation) {}
+        /* Animation interpolators. */
+        private final Interpolator mLogAccelerateInterpolator;
+        private final Interpolator mFastOutSlowInInterpolator;
+        private final Interpolator mLinearOutSlowInInterpolator;
+        private final Interpolator mFastOutLinearInInterpolator;
 
-                    @Override
-                    public void onAnimationEnd(Animation animation) {
-                        setMainPanelAsContent();
-                        mMainPanel.fadeIn(true);
-                    }
-
-                    @Override
-                    public void onAnimationRepeat(Animation animation) {
-                    }
-                };
+        /* Animations. */
+        private final AnimatorSet mShowAnimation;
         private final AnimatorSet mDismissAnimation;
         private final AnimatorSet mHideAnimation;
-        private final AnimationSet mOpenOverflowAnimation = new AnimationSet(true);
-        private final AnimationSet mCloseOverflowAnimation = new AnimationSet(true);
+        private final AnimationSet mOpenOverflowAnimation;
+        private final AnimationSet mCloseOverflowAnimation;
+        private final Animation.AnimationListener mOverflowAnimationListener;
 
-        private final Runnable mOpenOverflow = new Runnable() {
-            @Override
-            public void run() {
-                openOverflow();
-            }
-        };
-        private final Runnable mCloseOverflow = new Runnable() {
-            @Override
-            public void run() {
-                closeOverflow();
-            }
-        };
-
-        private final Rect mViewPortOnScreen = new Rect();
-        private final Point mCoordsOnWindow = new Point();
+        private final Rect mViewPortOnScreen = new Rect();  // portion of screen we can draw in.
+        private final Point mCoordsOnWindow = new Point();  // popup window coordinates.
+        /* Temporary data holders. Reset values before using. */
         private final int[] mTmpCoords = new int[2];
         private final Rect mTmpRect = new Rect();
 
@@ -357,12 +349,56 @@
                     }
                 };
 
+        /**
+         * @see OverflowPanelViewHelper#preparePopupContent().
+         */
+        private final Runnable mPreparePopupContentRTLHelper = new Runnable() {
+            @Override
+            public void run() {
+                setPanelsStatesAtRestingPosition();
+                setContentAreaAsTouchableSurface();
+                mContentContainer.setAlpha(1);
+            }
+        };
+
+        /* Runnable to reset the overflow button's drawable after an overflow transition. */
+        private final Runnable mResetOverflowButtonDrawable = new Runnable() {
+            @Override
+            public void run() {
+                if (mIsOverflowOpen) {
+                    mOverflowButton.setImageDrawable(mArrow);
+                } else {
+                    mOverflowButton.setImageDrawable(mOverflow);
+                }
+            }
+        };
+
         private boolean mDismissed = true; // tracks whether this popup is dismissed or dismissing.
         private boolean mHidden; // tracks whether this popup is hidden or hiding.
 
-        private FloatingToolbarOverflowPanel mOverflowPanel;
-        private FloatingToolbarMainPanel mMainPanel;
-        private int mOverflowDirection;
+        /* Calculated sizes for panels and overflow button. */
+        private final Size mOverflowButtonSize;
+        private Size mOverflowPanelSize;  // Should be null when there is no overflow.
+        private Size mMainPanelSize;
+
+        /* Item click listeners */
+        private MenuItem.OnMenuItemClickListener mOnMenuItemClickListener;
+        private final View.OnClickListener mMenuItemButtonOnClickListener =
+                new View.OnClickListener() {
+                    @Override
+                    public void onClick(View v) {
+                        if (v.getTag() instanceof MenuItem) {
+                            if (mOnMenuItemClickListener != null) {
+                                mOnMenuItemClickListener.onMenuItemClick((MenuItem) v.getTag());
+                            }
+                        }
+                    }
+                };
+
+        private boolean mOpenOverflowUpwards;  // Whether the overflow opens upwards or downwards.
+        private boolean mIsOverflowOpen;
+
+        private int mTransitionDurationScale;  // Used to scale the toolbar transition duration.
 
         /**
          * Initializes a new floating toolbar popup.
@@ -375,6 +411,48 @@
             mContext = Preconditions.checkNotNull(context);
             mContentContainer = createContentContainer(context);
             mPopupWindow = createPopupWindow(mContentContainer);
+            mMarginHorizontal = parent.getResources()
+                    .getDimensionPixelSize(R.dimen.floating_toolbar_horizontal_margin);
+            mMarginVertical = parent.getResources()
+                    .getDimensionPixelSize(R.dimen.floating_toolbar_vertical_margin);
+
+            // Interpolators
+            mLogAccelerateInterpolator = new LogAccelerateInterpolator();
+            mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(
+                    mContext, android.R.interpolator.fast_out_slow_in);
+            mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(
+                    mContext, android.R.interpolator.linear_out_slow_in);
+            mFastOutLinearInInterpolator = AnimationUtils.loadInterpolator(
+                    mContext, android.R.interpolator.fast_out_linear_in);
+
+            // Drawables. Needed for views.
+            mArrow = mContext.getResources()
+                    .getDrawable(R.drawable.ft_avd_tooverflow, mContext.getTheme());
+            mArrow.setAutoMirrored(true);
+            mOverflow = mContext.getResources()
+                    .getDrawable(R.drawable.ft_avd_toarrow, mContext.getTheme());
+            mOverflow.setAutoMirrored(true);
+            mToArrow = (AnimatedVectorDrawable) mContext.getResources()
+                    .getDrawable(R.drawable.ft_avd_toarrow_animation, mContext.getTheme());
+            mToArrow.setAutoMirrored(true);
+            mToOverflow = (AnimatedVectorDrawable) mContext.getResources()
+                    .getDrawable(R.drawable.ft_avd_tooverflow_animation, mContext.getTheme());
+            mToOverflow.setAutoMirrored(true);
+
+            // Views
+            mOverflowButton = createOverflowButton();
+            mOverflowButtonSize = measure(mOverflowButton);
+            mMainPanel = createMainPanel();
+            mOverflowPanelViewHelper = new OverflowPanelViewHelper(mContext);
+            mOverflowPanel = createOverflowPanel();
+
+            // Animation. Need views.
+            mOverflowAnimationListener = createOverflowAnimationListener();
+            mOpenOverflowAnimation = new AnimationSet(true);
+            mOpenOverflowAnimation.setAnimationListener(mOverflowAnimationListener);
+            mCloseOverflowAnimation = new AnimationSet(true);
+            mCloseOverflowAnimation.setAnimationListener(mOverflowAnimationListener);
+            mShowAnimation = createEnterAnimation(mContentContainer);
             mDismissAnimation = createExitAnimation(
                     mContentContainer,
                     150,  // startDelay
@@ -394,35 +472,23 @@
                             mPopupWindow.dismiss();
                         }
                     });
-            mMarginHorizontal = parent.getResources()
-                    .getDimensionPixelSize(R.dimen.floating_toolbar_horizontal_margin);
-            mMarginVertical = parent.getResources()
-                    .getDimensionPixelSize(R.dimen.floating_toolbar_vertical_margin);
         }
 
         /**
          * Lays out buttons for the specified menu items.
+         * Requires a subsequent call to {@link #show()} to show the items.
          */
         public void layoutMenuItems(
                 List<MenuItem> menuItems,
                 MenuItem.OnMenuItemClickListener menuItemClickListener,
                 int suggestedWidth) {
-            Preconditions.checkNotNull(menuItems);
-
-            mContentContainer.removeAllViews();
-            if (mMainPanel == null) {
-                mMainPanel = new FloatingToolbarMainPanel(mContext, mOpenOverflow);
-            }
-            List<MenuItem> overflowMenuItems =
-                    mMainPanel.layoutMenuItems(menuItems, getToolbarWidth(suggestedWidth));
-            mMainPanel.setOnMenuItemClickListener(menuItemClickListener);
-            if (!overflowMenuItems.isEmpty()) {
-                if (mOverflowPanel == null) {
-                    mOverflowPanel =
-                            new FloatingToolbarOverflowPanel(mContext, mCloseOverflow);
-                }
-                mOverflowPanel.setMenuItems(overflowMenuItems);
-                mOverflowPanel.setOnMenuItemClickListener(menuItemClickListener);
+            mOnMenuItemClickListener = menuItemClickListener;
+            cancelOverflowAnimations();
+            clearPanels();
+            menuItems = layoutMainPanelItems(menuItems, getAdjustedToolbarWidth(suggestedWidth));
+            if (!menuItems.isEmpty()) {
+                // Add remaining items to the overflow.
+                layoutOverflowPanelItems(menuItems);
             }
             updatePopupSize();
         }
@@ -443,20 +509,13 @@
             cancelDismissAndHideAnimations();
             cancelOverflowAnimations();
 
-            // Make sure a panel is set as the content.
-            if (mContentContainer.getChildCount() == 0) {
-                setMainPanelAsContent();
-                // If we're yet to show the popup, set the container visibility to zero.
-                // The "show" animation will make this visible.
-                mContentContainer.setAlpha(0);
-            }
             refreshCoordinatesAndOverflowDirection(contentRectOnScreen);
             preparePopupContent();
             // We need to specify the position in window coordinates.
             // TODO: Consider to use PopupWindow.setLayoutInScreenEnabled(true) so that we can
-            // specify the popup poision in screen coordinates.
-            mPopupWindow.showAtLocation(mParent, Gravity.NO_GRAVITY, mCoordsOnWindow.x,
-                    mCoordsOnWindow.y);
+            // specify the popup position in screen coordinates.
+            mPopupWindow.showAtLocation(
+                    mParent, Gravity.NO_GRAVITY, mCoordsOnWindow.x, mCoordsOnWindow.y);
             setTouchableSurfaceInsetsComputer();
             runShowAnimation();
         }
@@ -472,6 +531,7 @@
             mHidden = false;
             mDismissed = true;
             mHideAnimation.cancel();
+
             runDismissAnimation();
             setZeroTouchableSurface();
         }
@@ -521,104 +581,90 @@
             preparePopupContent();
             // We need to specify the position in window coordinates.
             // TODO: Consider to use PopupWindow.setLayoutInScreenEnabled(true) so that we can
-            // specify the popup poision in screen coordinates.
-            mPopupWindow.update(mCoordsOnWindow.x, mCoordsOnWindow.y, getWidth(), getHeight());
-        }
-
-        /**
-         * Returns the width of this popup.
-         */
-        public int getWidth() {
-            return mPopupWindow.getWidth();
-        }
-
-        /**
-         * Returns the height of this popup.
-         */
-        public int getHeight() {
-            return mPopupWindow.getHeight();
-        }
-
-        /**
-         * Returns the context this popup is running in.
-         */
-        public Context getContext() {
-            return mContext;
+            // specify the popup position in screen coordinates.
+            mPopupWindow.update(
+                    mCoordsOnWindow.x, mCoordsOnWindow.y,
+                    mPopupWindow.getWidth(), mPopupWindow.getHeight());
         }
 
         private void refreshCoordinatesAndOverflowDirection(Rect contentRectOnScreen) {
             refreshViewPort();
 
-            int x = contentRectOnScreen.centerX() - getWidth() / 2;
+            int x = contentRectOnScreen.centerX() - mPopupWindow.getWidth() / 2;
             // Update x so that the toolbar isn't rendered behind the nav bar in landscape.
-            x = Math.max(0, Math.min(x, mViewPortOnScreen.right - getWidth()));
+            x = Math.max(0, Math.min(x, mViewPortOnScreen.right - mPopupWindow.getWidth()));
 
-            int y;
+            final int y;
 
-            int availableHeightAboveContent = contentRectOnScreen.top - mViewPortOnScreen.top;
-            int availableHeightBelowContent = mViewPortOnScreen.bottom - contentRectOnScreen.bottom;
+            final int availableHeightAboveContent =
+                    contentRectOnScreen.top - mViewPortOnScreen.top;
+            final int availableHeightBelowContent =
+                    mViewPortOnScreen.bottom - contentRectOnScreen.bottom;
 
-            if (mOverflowPanel == null) {  // There is no overflow.
-                if (availableHeightAboveContent >= getToolbarHeightWithVerticalMargin()) {
+            final int margin = 2 * mMarginVertical;
+            final int toolbarHeightWithVerticalMargin = getLineHeight(mContext) + margin;
+
+            if (!hasOverflow()) {
+                if (availableHeightAboveContent >= toolbarHeightWithVerticalMargin) {
                     // There is enough space at the top of the content.
-                    y = contentRectOnScreen.top - getToolbarHeightWithVerticalMargin();
-                } else if (availableHeightBelowContent >= getToolbarHeightWithVerticalMargin()) {
+                    y = contentRectOnScreen.top - toolbarHeightWithVerticalMargin;
+                } else if (availableHeightBelowContent >= toolbarHeightWithVerticalMargin) {
                     // There is enough space at the bottom of the content.
                     y = contentRectOnScreen.bottom;
-                } else if (availableHeightBelowContent >= getEstimatedToolbarHeight(mContext)) {
+                } else if (availableHeightBelowContent >= getLineHeight(mContext)) {
                     // Just enough space to fit the toolbar with no vertical margins.
                     y = contentRectOnScreen.bottom - mMarginVertical;
                 } else {
                     // Not enough space. Prefer to position as high as possible.
                     y = Math.max(
                             mViewPortOnScreen.top,
-                            contentRectOnScreen.top - getToolbarHeightWithVerticalMargin());
+                            contentRectOnScreen.top - toolbarHeightWithVerticalMargin);
                 }
-            } else {  // There is an overflow.
-                int margin = 2 * mMarginVertical;
-                int minimumOverflowHeightWithMargin = mOverflowPanel.getMinimumHeight() + margin;
-                int availableHeightThroughContentDown = mViewPortOnScreen.bottom -
-                        contentRectOnScreen.top + getToolbarHeightWithVerticalMargin();
-                int availableHeightThroughContentUp = contentRectOnScreen.bottom -
-                        mViewPortOnScreen.top + getToolbarHeightWithVerticalMargin();
+            } else {
+                // Has an overflow.
+                final int minimumOverflowHeightWithMargin =
+                        calculateOverflowHeight(MIN_OVERFLOW_SIZE) + margin;
+                final int availableHeightThroughContentDown = mViewPortOnScreen.bottom -
+                        contentRectOnScreen.top + toolbarHeightWithVerticalMargin;
+                final int availableHeightThroughContentUp = contentRectOnScreen.bottom -
+                        mViewPortOnScreen.top + toolbarHeightWithVerticalMargin;
 
                 if (availableHeightAboveContent >= minimumOverflowHeightWithMargin) {
                     // There is enough space at the top of the content rect for the overflow.
                     // Position above and open upwards.
                     updateOverflowHeight(availableHeightAboveContent - margin);
-                    y = contentRectOnScreen.top - getHeight();
-                    mOverflowDirection = OVERFLOW_DIRECTION_UP;
-                } else if (availableHeightAboveContent >= getToolbarHeightWithVerticalMargin()
+                    y = contentRectOnScreen.top - mPopupWindow.getHeight();
+                    mOpenOverflowUpwards = true;
+                } else if (availableHeightAboveContent >= toolbarHeightWithVerticalMargin
                         && availableHeightThroughContentDown >= minimumOverflowHeightWithMargin) {
                     // There is enough space at the top of the content rect for the main panel
                     // but not the overflow.
                     // Position above but open downwards.
                     updateOverflowHeight(availableHeightThroughContentDown - margin);
-                    y = contentRectOnScreen.top - getToolbarHeightWithVerticalMargin();
-                    mOverflowDirection = OVERFLOW_DIRECTION_DOWN;
+                    y = contentRectOnScreen.top - toolbarHeightWithVerticalMargin;
+                    mOpenOverflowUpwards = false;
                 } else if (availableHeightBelowContent >= minimumOverflowHeightWithMargin) {
                     // There is enough space at the bottom of the content rect for the overflow.
                     // Position below and open downwards.
                     updateOverflowHeight(availableHeightBelowContent - margin);
                     y = contentRectOnScreen.bottom;
-                    mOverflowDirection = OVERFLOW_DIRECTION_DOWN;
-                } else if (availableHeightBelowContent >= getToolbarHeightWithVerticalMargin()
+                    mOpenOverflowUpwards = false;
+                } else if (availableHeightBelowContent >= toolbarHeightWithVerticalMargin
                         && mViewPortOnScreen.height() >= minimumOverflowHeightWithMargin) {
                     // There is enough space at the bottom of the content rect for the main panel
                     // but not the overflow.
                     // Position below but open upwards.
                     updateOverflowHeight(availableHeightThroughContentUp - margin);
-                    y = contentRectOnScreen.bottom + getToolbarHeightWithVerticalMargin() -
-                            getHeight();
-                    mOverflowDirection = OVERFLOW_DIRECTION_UP;
+                    y = contentRectOnScreen.bottom + toolbarHeightWithVerticalMargin -
+                            mPopupWindow.getHeight();
+                    mOpenOverflowUpwards = true;
                 } else {
                     // Not enough space.
                     // Position at the top of the view port and open downwards.
                     updateOverflowHeight(mViewPortOnScreen.height() - margin);
                     y = mViewPortOnScreen.top;
-                    mOverflowDirection = OVERFLOW_DIRECTION_DOWN;
+                    mOpenOverflowUpwards = false;
                 }
-                mOverflowPanel.setOverflowDirection(mOverflowDirection);
             }
 
             // We later specify the location of PopupWindow relative to the attached window.
@@ -639,15 +685,11 @@
             mCoordsOnWindow.set(x - windowLeftOnScreen, y - windowTopOnScreen);
         }
 
-        private int getToolbarHeightWithVerticalMargin() {
-            return getEstimatedToolbarHeight(mContext) + mMarginVertical * 2;
-        }
-
         /**
          * Performs the "show" animation on the floating popup.
          */
         private void runShowAnimation() {
-            createEnterAnimation(mContentContainer).start();
+            mShowAnimation.start();
         }
 
         /**
@@ -670,42 +712,16 @@
         }
 
         private void cancelOverflowAnimations() {
-            if (mOpenOverflowAnimation.hasStarted()
-                    && !mOpenOverflowAnimation.hasEnded()) {
-                // Remove the animation listener, stop the animation,
-                // then trigger the lister explicitly so it is not posted
-                // to the message queue.
-                mOpenOverflowAnimation.setAnimationListener(null);
-                mContentContainer.clearAnimation();
-                mOnOverflowOpened.onAnimationEnd(null);
-            }
-            if (mCloseOverflowAnimation.hasStarted()
-                    && !mCloseOverflowAnimation.hasEnded()) {
-                // Remove the animation listener, stop the animation,
-                // then trigger the lister explicitly so it is not posted
-                // to the message queue.
-                mCloseOverflowAnimation.setAnimationListener(null);
-                mContentContainer.clearAnimation();
-                mOnOverflowClosed.onAnimationEnd(null);
-            }
+            mContentContainer.clearAnimation();
+            mMainPanel.animate().cancel();
+            mOverflowPanel.animate().cancel();
+            mToArrow.stop();
+            mToOverflow.stop();
         }
 
-        /**
-         * Opens the floating toolbar overflow.
-         * This method should not be called if menu items have not been laid out with
-         * {@link #layoutMenuItems(java.util.List, MenuItem.OnMenuItemClickListener, int)}.
-         *
-         * @throws IllegalStateException if called when menu items have not been laid out.
-         */
         private void openOverflow() {
-            Preconditions.checkState(mMainPanel != null);
-            Preconditions.checkState(mOverflowPanel != null);
-
-            mMainPanel.fadeOut(true);
-            Size overflowPanelSize = mOverflowPanel.measure();
-            final int targetWidth = overflowPanelSize.getWidth();
-            final int targetHeight = overflowPanelSize.getHeight();
-            final boolean morphUpwards = (mOverflowDirection == OVERFLOW_DIRECTION_UP);
+            final int targetWidth = mOverflowPanelSize.getWidth();
+            final int targetHeight = mOverflowPanelSize.getHeight();
             final int startWidth = mContentContainer.getWidth();
             final int startHeight = mContentContainer.getHeight();
             final float startY = mContentContainer.getY();
@@ -714,230 +730,281 @@
             Animation widthAnimation = new Animation() {
                 @Override
                 protected void applyTransformation(float interpolatedTime, Transformation t) {
-                    ViewGroup.LayoutParams params = mContentContainer.getLayoutParams();
                     int deltaWidth = (int) (interpolatedTime * (targetWidth - startWidth));
-                    params.width = startWidth + deltaWidth;
-                    mContentContainer.setLayoutParams(params);
+                    setWidth(mContentContainer, startWidth + deltaWidth);
                     if (isRTL()) {
                         mContentContainer.setX(left);
+
+                        // Lock the panels in place.
+                        mMainPanel.setX(0);
+                        mOverflowPanel.setX(0);
                     } else {
                         mContentContainer.setX(right - mContentContainer.getWidth());
+
+                        // Offset the panels' positions so they look like they're locked in place
+                        // on the screen.
+                        mMainPanel.setX(mContentContainer.getWidth() - startWidth);
+                        mOverflowPanel.setX(mContentContainer.getWidth() - targetWidth);
                     }
                 }
             };
             Animation heightAnimation = new Animation() {
                 @Override
                 protected void applyTransformation(float interpolatedTime, Transformation t) {
-                    ViewGroup.LayoutParams params = mContentContainer.getLayoutParams();
                     int deltaHeight = (int) (interpolatedTime * (targetHeight - startHeight));
-                    params.height = startHeight + deltaHeight;
-                    mContentContainer.setLayoutParams(params);
-                    if (morphUpwards) {
-                        float y = startY - (mContentContainer.getHeight() - startHeight);
-                        mContentContainer.setY(y);
+                    setHeight(mContentContainer, startHeight + deltaHeight);
+                    if (mOpenOverflowUpwards) {
+                        mContentContainer.setY(
+                                startY - (mContentContainer.getHeight() - startHeight));
+                        positionContentYCoordinatesIfOpeningOverflowUpwards();
                     }
                 }
             };
-            widthAnimation.setDuration(240);
-            heightAnimation.setDuration(180);
-            heightAnimation.setStartOffset(60);
+            final float overflowButtonStartX = mOverflowButton.getX();
+            final float overflowButtonTargetX = isRTL() ?
+                    overflowButtonStartX + targetWidth - mOverflowButton.getWidth() :
+                    overflowButtonStartX - targetWidth + mOverflowButton.getWidth();
+            Animation overflowButtonAnimation = new Animation() {
+                @Override
+                protected void applyTransformation(float interpolatedTime, Transformation t) {
+                    float overflowButtonX = overflowButtonStartX
+                            + interpolatedTime * (overflowButtonTargetX - overflowButtonStartX);
+                    float deltaContainerWidth = isRTL() ?
+                            0 :
+                            mContentContainer.getWidth() - startWidth;
+                    float actualOverflowButtonX = overflowButtonX + deltaContainerWidth;
+                    mOverflowButton.setX(actualOverflowButtonX);
+                }
+            };
+            widthAnimation.setInterpolator(mLogAccelerateInterpolator);
+            widthAnimation.setDuration(getAdjustedDuration(250));
+            heightAnimation.setInterpolator(mFastOutSlowInInterpolator);
+            heightAnimation.setDuration(getAdjustedDuration(250));
+            overflowButtonAnimation.setInterpolator(mFastOutSlowInInterpolator);
+            overflowButtonAnimation.setDuration(getAdjustedDuration(250));
             mOpenOverflowAnimation.getAnimations().clear();
-            mOpenOverflowAnimation.setAnimationListener(mOnOverflowOpened);
+            mOpenOverflowAnimation.getAnimations().clear();
             mOpenOverflowAnimation.addAnimation(widthAnimation);
             mOpenOverflowAnimation.addAnimation(heightAnimation);
+            mOpenOverflowAnimation.addAnimation(overflowButtonAnimation);
             mContentContainer.startAnimation(mOpenOverflowAnimation);
+            mIsOverflowOpen = true;
+            mMainPanel.animate()
+                    .alpha(0).withLayer()
+                    .setInterpolator(mLinearOutSlowInInterpolator)
+                    .setDuration(250)
+                    .start();
+            mOverflowPanel.setAlpha(1); // fadeIn in 0ms.
         }
 
-        /**
-         * Opens the floating toolbar overflow.
-         * This method should not be called if menu items have not been laid out with
-         * {@link #layoutMenuItems(java.util.List, MenuItem.OnMenuItemClickListener, int)}.
-         *
-         * @throws IllegalStateException if called when menu items have not been laid out.
-         */
         private void closeOverflow() {
-            Preconditions.checkState(mMainPanel != null);
-            Preconditions.checkState(mOverflowPanel != null);
-
-            mOverflowPanel.fadeOut(true);
-            Size mainPanelSize = mMainPanel.measure();
-            final int targetWidth = mainPanelSize.getWidth();
-            final int targetHeight = mainPanelSize.getHeight();
+            final int targetWidth = mMainPanelSize.getWidth();
             final int startWidth = mContentContainer.getWidth();
-            final int startHeight = mContentContainer.getHeight();
-            final float bottom = mContentContainer.getY() + mContentContainer.getHeight();
-            final boolean morphedUpwards = (mOverflowDirection == OVERFLOW_DIRECTION_UP);
             final float left = mContentContainer.getX();
             final float right = left + mContentContainer.getWidth();
             Animation widthAnimation = new Animation() {
                 @Override
                 protected void applyTransformation(float interpolatedTime, Transformation t) {
-                    ViewGroup.LayoutParams params = mContentContainer.getLayoutParams();
                     int deltaWidth = (int) (interpolatedTime * (targetWidth - startWidth));
-                    params.width = startWidth + deltaWidth;
-                    mContentContainer.setLayoutParams(params);
+                    setWidth(mContentContainer, startWidth + deltaWidth);
                     if (isRTL()) {
                         mContentContainer.setX(left);
+
+                        // Lock the panels in place.
+                        mMainPanel.setX(0);
+                        mOverflowPanel.setX(0);
                     } else {
                         mContentContainer.setX(right - mContentContainer.getWidth());
+
+                        // Offset the panels' positions so they look like they're locked in place
+                        // on the screen.
+                        mMainPanel.setX(mContentContainer.getWidth() - targetWidth);
+                        mOverflowPanel.setX(mContentContainer.getWidth() - startWidth);
                     }
                 }
             };
+            final int targetHeight = mMainPanelSize.getHeight();
+            final int startHeight = mContentContainer.getHeight();
+            final float bottom = mContentContainer.getY() + mContentContainer.getHeight();
             Animation heightAnimation = new Animation() {
                 @Override
                 protected void applyTransformation(float interpolatedTime, Transformation t) {
-                    ViewGroup.LayoutParams params = mContentContainer.getLayoutParams();
                     int deltaHeight = (int) (interpolatedTime * (targetHeight - startHeight));
-                    params.height = startHeight + deltaHeight;
-                    mContentContainer.setLayoutParams(params);
-                    if (morphedUpwards) {
+                    setHeight(mContentContainer, startHeight + deltaHeight);
+                    if (mOpenOverflowUpwards) {
                         mContentContainer.setY(bottom - mContentContainer.getHeight());
+                        positionContentYCoordinatesIfOpeningOverflowUpwards();
                     }
                 }
             };
-            widthAnimation.setDuration(150);
-            widthAnimation.setStartOffset(150);
-            heightAnimation.setDuration(210);
+            final float overflowButtonStartX = mOverflowButton.getX();
+            final float overflowButtonTargetX = isRTL() ?
+                    overflowButtonStartX - startWidth + mOverflowButton.getWidth() :
+                    overflowButtonStartX + startWidth - mOverflowButton.getWidth();
+            Animation overflowButtonAnimation = new Animation() {
+                @Override
+                protected void applyTransformation(float interpolatedTime, Transformation t) {
+                    float overflowButtonX = overflowButtonStartX
+                            + interpolatedTime * (overflowButtonTargetX - overflowButtonStartX);
+                    float deltaContainerWidth = isRTL() ?
+                            0 :
+                            mContentContainer.getWidth() - startWidth;
+                    float actualOverflowButtonX = overflowButtonX + deltaContainerWidth;
+                    mOverflowButton.setX(actualOverflowButtonX);
+                }
+            };
+            widthAnimation.setInterpolator(mFastOutSlowInInterpolator);
+            widthAnimation.setDuration(getAdjustedDuration(250));
+            heightAnimation.setInterpolator(mLogAccelerateInterpolator);
+            heightAnimation.setDuration(getAdjustedDuration(250));
+            overflowButtonAnimation.setInterpolator(mFastOutSlowInInterpolator);
+            overflowButtonAnimation.setDuration(getAdjustedDuration(250));
             mCloseOverflowAnimation.getAnimations().clear();
-            mCloseOverflowAnimation.setAnimationListener(mOnOverflowClosed);
             mCloseOverflowAnimation.addAnimation(widthAnimation);
             mCloseOverflowAnimation.addAnimation(heightAnimation);
+            mCloseOverflowAnimation.addAnimation(overflowButtonAnimation);
             mContentContainer.startAnimation(mCloseOverflowAnimation);
+            mIsOverflowOpen = false;
+            mMainPanel.animate()
+                    .alpha(1).withLayer()
+                    .setInterpolator(mFastOutLinearInInterpolator)
+                    .setDuration(100)
+                    .start();
+            mOverflowPanel.animate()
+                    .alpha(0).withLayer()
+                    .setInterpolator(mLinearOutSlowInInterpolator)
+                    .setDuration(150)
+                    .start();
         }
 
-        /**
-         * Prepares the content container for show and update calls.
-         */
-        private void preparePopupContent() {
-            // Reset visibility.
-            if (mMainPanel != null) {
-                mMainPanel.fadeIn(false);
-            }
-            if (mOverflowPanel != null) {
-                mOverflowPanel.fadeIn(false);
-            }
+        private void setPanelsStatesAtRestingPosition() {
+            mOverflowButton.setEnabled(true);
 
-            // Reset position.
-            if (isMainPanelContent()) {
-                positionMainPanel();
-            }
-            if (isOverflowPanelContent()) {
-                positionOverflowPanel();
-            }
-        }
+            if (mIsOverflowOpen) {
+                // Set open state.
+                final Size containerSize = mOverflowPanelSize;
+                setSize(mContentContainer, containerSize);
+                mMainPanel.setAlpha(0);
+                mOverflowPanel.setAlpha(1);
+                mOverflowButton.setImageDrawable(mArrow);
 
-        private boolean isMainPanelContent() {
-            return mMainPanel != null
-                    && mContentContainer.getChildAt(0) == mMainPanel.getView();
-        }
+                // Update x-coordinates depending on RTL state.
+                if (isRTL()) {
+                    mContentContainer.setX(mMarginHorizontal);  // align left
+                    mMainPanel.setX(0);  // align left
+                    mOverflowButton.setX(  // align right
+                            containerSize.getWidth() - mOverflowButtonSize.getWidth());
+                    mOverflowPanel.setX(0);  // align left
+                } else {
+                    mContentContainer.setX(  // align right
+                            mMarginHorizontal +
+                                    mMainPanelSize.getWidth() - containerSize.getWidth());
+                    mMainPanel.setX(-mContentContainer.getX());  // align right
+                    mOverflowButton.setX(0);  // align left
+                    mOverflowPanel.setX(0);  // align left
+                }
 
-        private boolean isOverflowPanelContent() {
-            return mOverflowPanel != null
-                    && mContentContainer.getChildAt(0) == mOverflowPanel.getView();
-        }
-
-        /**
-         * Sets the current content to be the main view panel.
-         */
-        private void setMainPanelAsContent() {
-            // This should never be called if the main panel has not been initialized.
-            Preconditions.checkNotNull(mMainPanel);
-            mContentContainer.removeAllViews();
-            Size mainPanelSize = mMainPanel.measure();
-            ViewGroup.LayoutParams params = mContentContainer.getLayoutParams();
-            params.width = mainPanelSize.getWidth();
-            params.height = mainPanelSize.getHeight();
-            mContentContainer.setLayoutParams(params);
-            mContentContainer.addView(mMainPanel.getView());
-            setContentAreaAsTouchableSurface();
-        }
-
-        /**
-         * Sets the current content to be the overflow view panel.
-         */
-        private void setOverflowPanelAsContent() {
-            // This should never be called if the overflow panel has not been initialized.
-            Preconditions.checkNotNull(mOverflowPanel);
-            mContentContainer.removeAllViews();
-            Size overflowPanelSize = mOverflowPanel.measure();
-            ViewGroup.LayoutParams params = mContentContainer.getLayoutParams();
-            params.width = overflowPanelSize.getWidth();
-            params.height = overflowPanelSize.getHeight();
-            mContentContainer.setLayoutParams(params);
-            mContentContainer.addView(mOverflowPanel.getView());
-            setContentAreaAsTouchableSurface();
-        }
-
-        /**
-         * Places the main view panel at the appropriate resting coordinates.
-         */
-        private void positionMainPanel() {
-            Preconditions.checkNotNull(mMainPanel);
-            mContentContainer.setX(mMarginHorizontal);
-
-            float y = mMarginVertical;
-            if  (mOverflowDirection == OVERFLOW_DIRECTION_UP) {
-                y = getHeight()
-                        - (mMainPanel.getView().getMeasuredHeight() + mMarginVertical);
-            }
-            mContentContainer.setY(y);
-            setContentAreaAsTouchableSurface();
-        }
-
-        /**
-         * Places the main view panel at the appropriate resting coordinates.
-         */
-        private void positionOverflowPanel() {
-            Preconditions.checkNotNull(mOverflowPanel);
-            float x;
-            if (isRTL()) {
-                x = mMarginHorizontal;
+                // Update y-coordinates depending on overflow's open direction.
+                if (mOpenOverflowUpwards) {
+                    mContentContainer.setY(mMarginVertical);  // align top
+                    mMainPanel.setY(  // align bottom
+                            containerSize.getHeight() - mContentContainer.getHeight());
+                    mOverflowButton.setY(  // align bottom
+                            containerSize.getHeight() - mOverflowButtonSize.getHeight());
+                    mOverflowPanel.setY(0);  // align top
+                } else {
+                    // opens downwards.
+                    mContentContainer.setY(mMarginVertical);  // align top
+                    mMainPanel.setY(0);  // align top
+                    mOverflowButton.setY(0);  // align top
+                    mOverflowPanel.setY(mOverflowButtonSize.getHeight());  // align bottom
+                }
             } else {
-                x = mPopupWindow.getWidth()
-                    - (mOverflowPanel.getView().getMeasuredWidth() + mMarginHorizontal);
+                if (hasOverflow()) {
+                    // overflow not open. Set closed state.
+                    final Size containerSize = mMainPanelSize;
+                    setSize(mContentContainer, containerSize);
+                    mMainPanel.setAlpha(1);
+                    mOverflowPanel.setAlpha(0);
+                    mOverflowButton.setImageDrawable(mOverflow);
+
+                    // Update x-coordinates depending on RTL state.
+                    if (isRTL()) {
+                        mContentContainer.setX(mMarginHorizontal);  // align left
+                        mMainPanel.setX(0);  // align left
+                        mOverflowButton.setX(0);  // align left
+                        mOverflowPanel.setX(0);  // align left
+                    } else {
+                        mContentContainer.setX(mMarginHorizontal);  // align left
+                        mMainPanel.setX(0);  // align left
+                        mOverflowButton.setX(  // align right
+                                containerSize.getWidth() - mOverflowButtonSize.getWidth());
+                        mOverflowPanel.setX(  // align right
+                                containerSize.getWidth() - mOverflowPanelSize.getWidth());
+                    }
+
+                    // Update y-coordinates depending on overflow's open direction.
+                    if (mOpenOverflowUpwards) {
+                        mContentContainer.setY(  // align bottom
+                                mMarginVertical +
+                                        mOverflowPanelSize.getHeight() - containerSize.getHeight());
+                        mMainPanel.setY(0);  // align top
+                        mOverflowButton.setY(0);  // align top
+                        mOverflowPanel.setY(  // align bottom
+                                containerSize.getHeight() - mOverflowPanelSize.getHeight());
+                    } else {
+                        // opens downwards.
+                        mContentContainer.setY(mMarginVertical);  // align top
+                        mMainPanel.setY(0);  // align top
+                        mOverflowButton.setY(0);  // align top
+                        mOverflowPanel.setY(mOverflowButtonSize.getHeight());  // align bottom
+                    }
+                } else {
+                    mContentContainer.setX(mMarginHorizontal);
+                    mContentContainer.setY(mMarginVertical);
+                }
             }
-            mContentContainer.setX(x);
-            mContentContainer.setY(mMarginVertical);
-            setContentAreaAsTouchableSurface();
         }
 
-        private void updateOverflowHeight(int height) {
-            if (mOverflowPanel != null) {
-                mOverflowPanel.setSuggestedHeight(height);
-
-                // Re-measure the popup and it's contents.
-                boolean mainPanelContent = isMainPanelContent();
-                boolean overflowPanelContent = isOverflowPanelContent();
-                mContentContainer.removeAllViews();  // required to update popup size.
+        private void updateOverflowHeight(int suggestedHeight) {
+            if (hasOverflow()) {
+                final int maxItemSize = (suggestedHeight - mOverflowButtonSize.getHeight()) /
+                        getLineHeight(mContext);
+                final int newHeight = calculateOverflowHeight(maxItemSize);
+                if (mOverflowPanelSize.getHeight() != newHeight) {
+                    mOverflowPanelSize = new Size(mOverflowPanelSize.getWidth(), newHeight);
+                }
+                setSize(mOverflowPanel, mOverflowPanelSize);
+                if (mIsOverflowOpen) {
+                    setSize(mContentContainer, mOverflowPanelSize);
+                    if (mOpenOverflowUpwards) {
+                        final int deltaHeight = mOverflowPanelSize.getHeight() - newHeight;
+                        mContentContainer.setY(mContentContainer.getY() + deltaHeight);
+                        mOverflowButton.setY(mOverflowButton.getY() - deltaHeight);
+                    }
+                } else {
+                    setSize(mContentContainer, mMainPanelSize);
+                }
                 updatePopupSize();
-                // Reset the appropriate content.
-                if (mainPanelContent) {
-                    setMainPanelAsContent();
-                }
-                if (overflowPanelContent) {
-                    setOverflowPanelAsContent();
-                }
             }
         }
 
         private void updatePopupSize() {
             int width = 0;
             int height = 0;
-            if (mMainPanel != null) {
-                Size mainPanelSize = mMainPanel.measure();
-                width = mainPanelSize.getWidth();
-                height = mainPanelSize.getHeight();
+            if (mMainPanelSize != null) {
+                width = Math.max(width, mMainPanelSize.getWidth());
+                height = Math.max(height, mMainPanelSize.getHeight());
             }
-            if (mOverflowPanel != null) {
-                Size overflowPanelSize = mOverflowPanel.measure();
-                width = Math.max(width, overflowPanelSize.getWidth());
-                height = Math.max(height, overflowPanelSize.getHeight());
+            if (mOverflowPanelSize != null) {
+                width = Math.max(width, mOverflowPanelSize.getWidth());
+                height = Math.max(height, mOverflowPanelSize.getHeight());
             }
             mPopupWindow.setWidth(width + mMarginHorizontal * 2);
             mPopupWindow.setHeight(height + mMarginVertical * 2);
+            maybeComputeTransitionDurationScale();
         }
 
-
         private void refreshViewPort() {
             mParent.getWindowVisibleDisplayFrame(mViewPortOnScreen);
         }
@@ -947,7 +1014,7 @@
             return !mTmpRect.equals(mViewPortOnScreen);
         }
 
-        private int getToolbarWidth(int suggestedWidth) {
+        private int getAdjustedToolbarWidth(int suggestedWidth) {
             int width = suggestedWidth;
             refreshViewPort();
             int maximumWidth = mViewPortOnScreen.width() - 2 * mParent.getResources()
@@ -971,11 +1038,17 @@
          * Sets the touchable region of this popup to be the area occupied by its content.
          */
         private void setContentAreaAsTouchableSurface() {
-            if (!mPopupWindow.isShowing()) {
-                mContentContainer.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+            Preconditions.checkNotNull(mMainPanelSize);
+            final int width;
+            final int height;
+            if (mIsOverflowOpen) {
+                Preconditions.checkNotNull(mOverflowPanelSize);
+                width = mOverflowPanelSize.getWidth();
+                height = mOverflowPanelSize.getHeight();
+            } else {
+                width = mMainPanelSize.getWidth();
+                height = mMainPanelSize.getHeight();
             }
-            int width = mContentContainer.getMeasuredWidth();
-            int height = mContentContainer.getMeasuredHeight();
             mTouchableRegion.set(
                     (int) mContentContainer.getX(),
                     (int) mContentContainer.getY(),
@@ -997,45 +1070,12 @@
         }
 
         private boolean isRTL() {
-            return mContentContainer.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
+            return mContext.getResources().getConfiguration().getLayoutDirection()
+                    == View.LAYOUT_DIRECTION_RTL;
         }
-    }
 
-    /**
-     * A widget that holds the primary menu items in the floating toolbar.
-     */
-    private static final class FloatingToolbarMainPanel {
-
-        private final Context mContext;
-        private final ViewGroup mContentView;
-        private final View.OnClickListener mMenuItemButtonOnClickListener =
-                new View.OnClickListener() {
-                    @Override
-                    public void onClick(View v) {
-                        if (v.getTag() instanceof MenuItem) {
-                            if (mOnMenuItemClickListener != null) {
-                                mOnMenuItemClickListener.onMenuItemClick((MenuItem) v.getTag());
-                            }
-                        }
-                    }
-                };
-        private final ViewFader viewFader;
-        private final Runnable mOpenOverflow;
-
-        private View mOpenOverflowButton;
-        private MenuItem.OnMenuItemClickListener mOnMenuItemClickListener;
-
-        /**
-         * Initializes a floating toolbar popup main view panel.
-         *
-         * @param context
-         * @param openOverflow  The code that opens the toolbar popup overflow.
-         */
-        public FloatingToolbarMainPanel(Context context, Runnable openOverflow) {
-            mContext = Preconditions.checkNotNull(context);
-            mContentView = new LinearLayout(context);
-            viewFader = new ViewFader(mContentView);
-            mOpenOverflow = Preconditions.checkNotNull(openOverflow);
+        private boolean hasOverflow() {
+            return mOverflowPanelSize != null;
         }
 
         /**
@@ -1044,16 +1084,14 @@
          *
          * @return The menu items that are not included in this main panel.
          */
-        public List<MenuItem> layoutMenuItems(List<MenuItem> menuItems, int width) {
+        public List<MenuItem> layoutMainPanelItems(
+                List<MenuItem> menuItems, final int toolbarWidth) {
             Preconditions.checkNotNull(menuItems);
 
-            // Reserve space for the "open overflow" button.
-            final int toolbarWidth = width - getEstimatedOpenOverflowButtonWidth(mContext);
-
             int availableWidth = toolbarWidth;
             final LinkedList<MenuItem> remainingMenuItems = new LinkedList<MenuItem>(menuItems);
 
-            mContentView.removeAllViews();
+            mMainPanel.removeAllViews();
 
             boolean isFirstItem = true;
             while (!remainingMenuItems.isEmpty()) {
@@ -1081,59 +1119,119 @@
 
                 menuItemButton.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
                 int menuItemButtonWidth = Math.min(menuItemButton.getMeasuredWidth(), toolbarWidth);
-                if (menuItemButtonWidth <= availableWidth) {
+                // Check if we can fit an item while reserving space for the overflowButton.
+                boolean canFitWithOverflow =
+                        menuItemButtonWidth <= availableWidth - mOverflowButtonSize.getWidth();
+                boolean canFitNoOverflow =
+                        remainingMenuItems.size() == 1 && menuItemButtonWidth <= availableWidth;
+                if (canFitWithOverflow || canFitNoOverflow) {
                     setButtonTagAndClickListener(menuItemButton, menuItem);
-                    mContentView.addView(menuItemButton);
+                    mMainPanel.addView(menuItemButton);
                     ViewGroup.LayoutParams params = menuItemButton.getLayoutParams();
                     params.width = menuItemButtonWidth;
                     menuItemButton.setLayoutParams(params);
                     availableWidth -= menuItemButtonWidth;
                     remainingMenuItems.pop();
                 } else {
-                    if (mOpenOverflowButton == null) {
-                        mOpenOverflowButton = LayoutInflater.from(mContext)
-                                .inflate(R.layout.floating_popup_open_overflow_button, null);
-                        mOpenOverflowButton.setOnClickListener(new View.OnClickListener() {
-                            @Override
-                            public void onClick(View v) {
-                                if (mOpenOverflowButton != null) {
-                                    mOpenOverflow.run();
-                                }
-                            }
-                        });
-                    }
-                    mContentView.addView(mOpenOverflowButton);
+                    // Reserve space for overflowButton.
+                    mMainPanel.setPaddingRelative(0, 0, mOverflowButtonSize.getWidth(), 0);
                     break;
                 }
             }
+            mMainPanelSize = measure(mMainPanel);
             return remainingMenuItems;
         }
 
-        public void setOnMenuItemClickListener(MenuItem.OnMenuItemClickListener listener) {
-            mOnMenuItemClickListener = listener;
-        }
+        private void layoutOverflowPanelItems(List<MenuItem> menuItems) {
+            ArrayAdapter<MenuItem> overflowPanelAdapter =
+                    (ArrayAdapter<MenuItem>) mOverflowPanel.getAdapter();
+            overflowPanelAdapter.clear();
+            final int size = menuItems.size();
+            for (int i = 0; i < size; i++) {
+                overflowPanelAdapter.add(menuItems.get(i));
+            }
+            mOverflowPanel.setAdapter(overflowPanelAdapter);
+            if (mOpenOverflowUpwards) {
+                mOverflowPanel.setY(0);
+            } else {
+                mOverflowPanel.setY(mOverflowButtonSize.getHeight());
+            }
 
-        public View getView() {
-            return mContentView;
-        }
-
-        public void fadeIn(boolean animate) {
-            viewFader.fadeIn(animate);
-        }
-
-        public void fadeOut(boolean animate) {
-            viewFader.fadeOut(animate);
+            int width = Math.max(getOverflowWidth(), mOverflowButtonSize.getWidth());
+            int height = calculateOverflowHeight(MAX_OVERFLOW_SIZE);
+            mOverflowPanelSize = new Size(width, height);
+            setSize(mOverflowPanel, mOverflowPanelSize);
         }
 
         /**
-         * Returns how big this panel's view should be.
-         * This method should only be called when the view has not been attached to a parent
-         * otherwise it will throw an illegal state.
+         * Resets the content container and appropriately position it's panels.
          */
-        public Size measure() throws IllegalStateException {
-            Preconditions.checkState(mContentView.getParent() == null);
-            mContentView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
-            return new Size(mContentView.getMeasuredWidth(), mContentView.getMeasuredHeight());
+        private void preparePopupContent() {
+            mContentContainer.removeAllViews();
+
+            // Add views in the specified order so they stack up as expected.
+            // Order: overflowPanel, mainPanel, overflowButton.
+            if (hasOverflow()) {
+                mContentContainer.addView(mOverflowPanel);
+            }
+            mContentContainer.addView(mMainPanel);
+            if (hasOverflow()) {
+                mContentContainer.addView(mOverflowButton);
+            }
+            setPanelsStatesAtRestingPosition();
+            setContentAreaAsTouchableSurface();
+
+            // The positioning of contents in RTL is wrong when the view is first rendered.
+            // Hide the view and post a runnable to recalculate positions and render the view.
+            // TODO: Investigate why this happens and fix.
+            if (isRTL()) {
+                mContentContainer.setAlpha(0);
+                mContentContainer.post(mPreparePopupContentRTLHelper);
+            }
+        }
+
+        /**
+         * Clears out the panels and their container. Resets their calculated sizes.
+         */
+        private void clearPanels() {
+            mOverflowPanelSize = null;
+            mMainPanelSize = null;
+            mIsOverflowOpen = false;
+            mMainPanel.removeAllViews();
+            ArrayAdapter<MenuItem> overflowPanelAdapter =
+                    (ArrayAdapter<MenuItem>) mOverflowPanel.getAdapter();
+            overflowPanelAdapter.clear();
+            mOverflowPanel.setAdapter(overflowPanelAdapter);
+            mContentContainer.removeAllViews();
+        }
+
+        private void positionContentYCoordinatesIfOpeningOverflowUpwards() {
+            if (mOpenOverflowUpwards) {
+                mMainPanel.setY(mContentContainer.getHeight() - mMainPanelSize.getHeight());
+                mOverflowButton.setY(mContentContainer.getHeight() - mOverflowButton.getHeight());
+                mOverflowPanel.setY(mContentContainer.getHeight() - mOverflowPanelSize.getHeight());
+            }
+        }
+
+        private int getOverflowWidth() {
+            int overflowWidth = 0;
+            final int count = mOverflowPanel.getAdapter().getCount();
+            for (int i = 0; i < count; i++) {
+                MenuItem menuItem = (MenuItem) mOverflowPanel.getAdapter().getItem(i);
+                overflowWidth =
+                        Math.max(mOverflowPanelViewHelper.calculateWidth(menuItem), overflowWidth);
+            }
+            return overflowWidth;
+        }
+
+        private int calculateOverflowHeight(int maxItemSize) {
+            // Maximum of 4 items, minimum of 2 if the overflow has to scroll.
+            int actualSize = Math.min(
+                    MAX_OVERFLOW_SIZE,
+                    Math.min(
+                            Math.max(MIN_OVERFLOW_SIZE, maxItemSize),
+                            mOverflowPanel.getCount()));
+            return actualSize * getLineHeight(mContext) + mOverflowButtonSize.getHeight();
         }
 
         private void setButtonTagAndClickListener(View menuItemButton, MenuItem menuItem) {
@@ -1144,281 +1242,326 @@
             button.setTag(menuItem);
             button.setOnClickListener(mMenuItemButtonOnClickListener);
         }
-    }
-
-
-    /**
-     * A widget that holds the overflow items in the floating toolbar.
-     */
-    private static final class FloatingToolbarOverflowPanel {
-
-        private final LinearLayout mContentView;
-        private final ViewGroup mBackButtonContainer;
-        private final View mBackButton;
-        private final ListView mListView;
-        private final TextView mListViewItemWidthCalculator;
-        private final ViewFader mViewFader;
-        private final Runnable mCloseOverflow;
-
-        private MenuItem.OnMenuItemClickListener mOnMenuItemClickListener;
-        private int mOverflowWidth;
-        private int mSuggestedHeight;
 
         /**
-         * Initializes a floating toolbar popup overflow view panel.
-         *
-         * @param context
-         * @param closeOverflow  The code that closes the toolbar popup's overflow.
+         * NOTE: Use only in android.view.animation.* animations. Do not use in android.animation.*
+         * animations. See comment about this in the code.
          */
-        public FloatingToolbarOverflowPanel(Context context, Runnable closeOverflow) {
-            mCloseOverflow = Preconditions.checkNotNull(closeOverflow);
+        private int getAdjustedDuration(int originalDuration) {
+            if (mTransitionDurationScale < 150) {
+                // For smaller transition, decrease the time.
+                return Math.max(originalDuration - 50, 0);
+            } else if (mTransitionDurationScale > 300) {
+                // For bigger transition, increase the time.
+                return originalDuration + 50;
+            }
 
-            mContentView = new LinearLayout(context);
-            mContentView.setOrientation(LinearLayout.VERTICAL);
-            mViewFader = new ViewFader(mContentView);
+            // Scale the animation duration with getDurationScale(). This allows
+            // android.view.animation.* animations to scale just like android.animation.* animations
+            // when  animator duration scale is adjusted in "Developer Options".
+            // For this reason, do not use this method for android.animation.* animations.
+            return (int) (originalDuration * ValueAnimator.getDurationScale());
+        }
 
-            mBackButton = LayoutInflater.from(context)
-                    .inflate(R.layout.floating_popup_close_overflow_button, null);
-            mBackButton.setOnClickListener(new View.OnClickListener() {
+        private void maybeComputeTransitionDurationScale() {
+            if (mMainPanelSize == null || mOverflowPanel == null) {
+                int w = mMainPanelSize.getWidth() - mOverflowPanelSize.getWidth();
+                int h = mOverflowPanelSize.getHeight() - mMainPanelSize.getHeight();
+                mTransitionDurationScale = (int) (Math.sqrt(w * w + h * h) /
+                        mContentContainer.getContext().getResources().getDisplayMetrics().density);
+            }
+        }
+
+        private ViewGroup createMainPanel() {
+            ViewGroup mainPanel = new LinearLayout(mContext) {
+                @Override
+                protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+                    if (isOverflowAnimating()) {
+                        // Update widthMeasureSpec to make sure that this view is not clipped
+                        // as we offset it's coordinates with respect to it's parent.
+                        widthMeasureSpec = MeasureSpec.makeMeasureSpec(
+                                mMainPanelSize.getWidth(),
+                                MeasureSpec.EXACTLY);
+                    }
+                    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+                }
+
+                @Override
+                public boolean onInterceptTouchEvent(MotionEvent ev) {
+                    // Intercept the touch event while the overflow is animating.
+                    return isOverflowAnimating();
+                }
+            };
+            return mainPanel;
+        }
+
+        private ImageButton createOverflowButton() {
+            final ImageButton overflowButton = (ImageButton) LayoutInflater.from(mContext)
+                    .inflate(R.layout.floating_popup_overflow_button, null);
+            overflowButton.setImageDrawable(mOverflow);
+            overflowButton.setOnClickListener(new View.OnClickListener() {
                 @Override
                 public void onClick(View v) {
-                    mCloseOverflow.run();
+                    final Drawable drawable = overflowButton.getDrawable();
+                    if (mIsOverflowOpen) {
+                        overflowButton.setImageDrawable(mToOverflow);
+                        mToOverflow.start();
+                        closeOverflow();
+                    } else {
+                        overflowButton.setImageDrawable(mToArrow);
+                        mToArrow.start();
+                        openOverflow();
+                    }
+                    overflowButton.postDelayed(
+                            mResetOverflowButtonDrawable, OVERFLOW_BUTTON_ANIMATION_DELAY);
                 }
             });
-            mBackButtonContainer = new LinearLayout(context);
-            mBackButtonContainer.addView(mBackButton);
+            return overflowButton;
+        }
 
-            mListView = createOverflowListView();
-            mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+        private ListView createOverflowPanel() {
+            final ListView overflowPanel = new ListView(FloatingToolbarPopup.this.mContext) {
+                @Override
+                protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+                    // Update heightMeasureSpec to make sure that this view is not clipped
+                    // as we offset it's coordinates with respect to it's parent.
+                    heightMeasureSpec = MeasureSpec.makeMeasureSpec(
+                            mOverflowPanelSize.getHeight() - mOverflowButtonSize.getHeight(),
+                            MeasureSpec.EXACTLY);
+                    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+                }
+
+                @Override
+                public boolean dispatchTouchEvent(MotionEvent ev) {
+                    if (isOverflowAnimating()) {
+                        // Eat the touch event.
+                        return true;
+                    }
+                    return super.dispatchTouchEvent(ev);
+                }
+            };
+            overflowPanel.setLayoutParams(new ViewGroup.LayoutParams(
+                    ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
+            overflowPanel.setDivider(null);
+            overflowPanel.setDividerHeight(0);
+
+            final ArrayAdapter adapter =
+                    new ArrayAdapter<MenuItem>(mContext, 0) {
+                        @Override
+                        public int getViewTypeCount() {
+                            return mOverflowPanelViewHelper.getViewTypeCount();
+                        }
+
+                        @Override
+                        public int getItemViewType(int position) {
+                            return mOverflowPanelViewHelper.getItemViewType(getItem(position));
+                        }
+
+                        @Override
+                        public View getView(int position, View convertView, ViewGroup parent) {
+                            return mOverflowPanelViewHelper.getView(
+                                    getItem(position), mOverflowPanelSize.getWidth(), convertView);
+                        }
+                    };
+            overflowPanel.setAdapter(adapter);
+
+            overflowPanel.setOnItemClickListener(new AdapterView.OnItemClickListener() {
                 @Override
                 public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
-                    MenuItem menuItem = (MenuItem) mListView.getAdapter().getItem(position);
+                    MenuItem menuItem = (MenuItem) overflowPanel.getAdapter().getItem(position);
                     if (mOnMenuItemClickListener != null) {
                         mOnMenuItemClickListener.onMenuItemClick(menuItem);
                     }
                 }
             });
 
-            mContentView.addView(mListView);
-            mContentView.addView(mBackButtonContainer);
+            return overflowPanel;
+        }
 
-            mListViewItemWidthCalculator = createOverflowMenuItemButton(context);
-            mListViewItemWidthCalculator.setLayoutParams(new ViewGroup.LayoutParams(
-                    ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
+        private boolean isOverflowAnimating() {
+            final boolean overflowOpening = mOpenOverflowAnimation.hasStarted()
+                    && !mOpenOverflowAnimation.hasEnded();
+            final boolean overflowClosing = mCloseOverflowAnimation.hasStarted()
+                    && !mCloseOverflowAnimation.hasEnded();
+            return overflowOpening || overflowClosing;
+        }
+
+        private Animation.AnimationListener createOverflowAnimationListener() {
+            Animation.AnimationListener listener = new Animation.AnimationListener() {
+                @Override
+                public void onAnimationStart(Animation animation) {
+                    // Disable the overflow button while it's animating.
+                    // It will be re-enabled when the animation stops.
+                    mOverflowButton.setEnabled(false);
+                }
+
+                @Override
+                public void onAnimationEnd(Animation animation) {
+                    // Posting this because it seems like this is called before the animation
+                    // actually ends.
+                    mContentContainer.post(new Runnable() {
+                        @Override
+                        public void run() {
+                            setPanelsStatesAtRestingPosition();
+                            setContentAreaAsTouchableSurface();
+                        }
+                    });
+                }
+
+                @Override
+                public void onAnimationRepeat(Animation animation) {
+                }
+            };
+            return listener;
+        }
+
+        private static Size measure(View view) {
+            Preconditions.checkState(view.getParent() == null);
+            view.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+            return new Size(view.getMeasuredWidth(), view.getMeasuredHeight());
+        }
+
+        private static void setSize(View view, int width, int height) {
+            view.setMinimumWidth(width);
+            view.setMinimumHeight(height);
+            ViewGroup.LayoutParams params = view.getLayoutParams();
+            params = (params == null) ? new ViewGroup.LayoutParams(0, 0) : params;
+            params.width = width;
+            params.height = height;
+            view.setLayoutParams(params);
+        }
+
+        private static void setSize(View view, Size size) {
+            setSize(view, size.getWidth(), size.getHeight());
+        }
+
+        private static void setWidth(View view, int width) {
+            ViewGroup.LayoutParams params = view.getLayoutParams();
+            setSize(view, width, params.height);
+        }
+
+        private static void setHeight(View view, int height) {
+            ViewGroup.LayoutParams params = view.getLayoutParams();
+            setSize(view, params.width, height);
+        }
+
+        private static int getLineHeight(Context context) {
+            return context.getResources().getDimensionPixelSize(R.dimen.floating_toolbar_height);
         }
 
         /**
-         * Sets the menu items to be displayed in the overflow.
+         * A custom interpolator used for various floating toolbar animations.
          */
-        public void setMenuItems(List<MenuItem> menuItems) {
-            ArrayAdapter overflowListViewAdapter = (ArrayAdapter) mListView.getAdapter();
-            overflowListViewAdapter.clear();
-            overflowListViewAdapter.addAll(menuItems);
-            setListViewHeight();
-            setOverflowWidth();
-        }
+        private static final class LogAccelerateInterpolator implements Interpolator {
 
-        public void setOnMenuItemClickListener(MenuItem.OnMenuItemClickListener listener) {
-            mOnMenuItemClickListener = listener;
-        }
+            private static final int BASE = 100;
+            private static final float LOGS_SCALE = 1f / computeLog(1, BASE);
 
-        /**
-         * Notifies the overflow of the current direction in which the overflow will be opened.
-         *
-         * @param overflowDirection  {@link FloatingToolbarPopup#OVERFLOW_DIRECTION_UP}
-         *   or {@link FloatingToolbarPopup#OVERFLOW_DIRECTION_DOWN}.
-         */
-        public void setOverflowDirection(int overflowDirection) {
-            mContentView.removeView(mBackButtonContainer);
-            int index = (overflowDirection == FloatingToolbarPopup.OVERFLOW_DIRECTION_UP)? 1 : 0;
-            mContentView.addView(mBackButtonContainer, index);
-        }
-
-        public void setSuggestedHeight(int height) {
-            mSuggestedHeight = height;
-            setListViewHeight();
-        }
-
-        public int getMinimumHeight() {
-            return mContentView.getContext().getResources().
-                    getDimensionPixelSize(R.dimen.floating_toolbar_minimum_overflow_height)
-                    + getEstimatedToolbarHeight(mContentView.getContext());
-        }
-
-        /**
-         * Returns the content view of the overflow.
-         */
-        public View getView() {
-            return mContentView;
-        }
-
-        public void fadeIn(boolean animate) {
-            mViewFader.fadeIn(animate);
-        }
-
-        public void fadeOut(boolean animate) {
-            mViewFader.fadeOut(animate);
-        }
-
-        /**
-         * Returns how big this panel's view should be.
-         * This method should only be called when the view has not been attached to a parent.
-         *
-         * @throws IllegalStateException
-         */
-        public Size measure() {
-            Preconditions.checkState(mContentView.getParent() == null);
-            mContentView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
-            return new Size(mContentView.getMeasuredWidth(), mContentView.getMeasuredHeight());
-        }
-
-        private void setListViewHeight() {
-            int itemHeight = getEstimatedToolbarHeight(mContentView.getContext());
-            int height = mListView.getAdapter().getCount() * itemHeight;
-            int maxHeight = mContentView.getContext().getResources().
-                    getDimensionPixelSize(R.dimen.floating_toolbar_maximum_overflow_height);
-            int minHeight = mContentView.getContext().getResources().
-                    getDimensionPixelSize(R.dimen.floating_toolbar_minimum_overflow_height);
-            int suggestedListViewHeight = mSuggestedHeight - (mSuggestedHeight % itemHeight)
-                    - itemHeight;  // reserve space for the back button.
-            ViewGroup.LayoutParams params = mListView.getLayoutParams();
-            if (suggestedListViewHeight <= 0) {
-                // Invalid height. Use the maximum height available.
-                params.height = Math.min(maxHeight, height);
-            } else if (suggestedListViewHeight < minHeight) {
-                // Height is smaller than minimum allowed. Use minimum height.
-                params.height = minHeight;
-            } else {
-                // Use the suggested height. Cap it at the maximum available height.
-                params.height = Math.min(Math.min(suggestedListViewHeight, maxHeight), height);
+            private static float computeLog(float t, int base) {
+                return (float) (1 - Math.pow(base, -t));
             }
-            mListView.setLayoutParams(params);
+
+            @Override
+            public float getInterpolation(float t) {
+                return 1 - computeLog(1 - t, BASE) * LOGS_SCALE;
+            }
         }
 
-        private void setOverflowWidth() {
-            mOverflowWidth = 0;
-            for (int i = 0; i < mListView.getAdapter().getCount(); i++) {
-                MenuItem menuItem = (MenuItem) mListView.getAdapter().getItem(i);
+        /**
+         * A helper for generating views for the overflow panel.
+         */
+        private static final class OverflowPanelViewHelper {
+
+            private static final int NUM_OF_VIEW_TYPES = 2;
+            private static final int VIEW_TYPE_STRING_TITLE = 0;
+            private static final int VIEW_TYPE_ICON_ONLY = 1;
+
+            private final TextView mStringTitleViewCalculator;
+            private final View mIconOnlyViewCalculator;
+
+            private final Context mContext;
+
+            public OverflowPanelViewHelper(Context context) {
+                mContext = Preconditions.checkNotNull(context);
+                mStringTitleViewCalculator = getStringTitleView(null, 0, null);
+                mIconOnlyViewCalculator = getIconOnlyView(null, 0, null);
+            }
+
+            public int getViewTypeCount() {
+                return NUM_OF_VIEW_TYPES;
+            }
+
+            public View getView(MenuItem menuItem, int minimumWidth, View convertView) {
                 Preconditions.checkNotNull(menuItem);
-                mListViewItemWidthCalculator.setText(menuItem.getTitle());
-                mListViewItemWidthCalculator.measure(
-                        MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
-                mOverflowWidth = Math.max(
-                        mListViewItemWidthCalculator.getMeasuredWidth(), mOverflowWidth);
+                if (getItemViewType(menuItem) == VIEW_TYPE_ICON_ONLY) {
+                    return getIconOnlyView(menuItem, minimumWidth, convertView);
+                }
+                return getStringTitleView(menuItem, minimumWidth, convertView);
             }
-        }
 
-        private ListView createOverflowListView() {
-            final Context context = mContentView.getContext();
-            final ListView overflowListView = new ListView(context);
-            overflowListView.setLayoutParams(new ViewGroup.LayoutParams(
-                    ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
-            overflowListView.setDivider(null);
-            overflowListView.setDividerHeight(0);
-
-            final int viewTypeCount = 2;
-            final int stringLabelViewType = 0;
-            final int iconOnlyViewType = 1;
-            final ArrayAdapter overflowListViewAdapter =
-                    new ArrayAdapter<MenuItem>(context, 0) {
-                        @Override
-                        public int getViewTypeCount() {
-                            return viewTypeCount;
-                        }
-
-                        @Override
-                        public int getItemViewType(int position) {
-                            if (isIconOnlyMenuItem(getItem(position))) {
-                                return iconOnlyViewType;
-                            }
-                            return stringLabelViewType;
-                        }
-
-                        @Override
-                        public View getView(int position, View convertView, ViewGroup parent) {
-                            if (getItemViewType(position) == iconOnlyViewType) {
-                                return getIconOnlyView(position, convertView);
-                            }
-                            return getStringTitleView(position, convertView);
-                        }
-
-                        private View getStringTitleView(int position, View convertView) {
-                            TextView menuButton;
-                            if (convertView != null) {
-                                menuButton = (TextView) convertView;
-                            } else {
-                                menuButton = createOverflowMenuItemButton(context);
-                            }
-                            MenuItem menuItem = getItem(position);
-                            menuButton.setText(menuItem.getTitle());
-                            menuButton.setContentDescription(menuItem.getTitle());
-                            menuButton.setMinimumWidth(mOverflowWidth);
-                            return menuButton;
-                        }
-
-                        private View getIconOnlyView(int position, View convertView) {
-                            View menuButton;
-                            if (convertView != null) {
-                                menuButton = convertView;
-                            } else {
-                                menuButton = LayoutInflater.from(context).inflate(
-                                        R.layout.floating_popup_overflow_image_list_item, null);
-                            }
-                            MenuItem menuItem = getItem(position);
-                            ((ImageView) menuButton
-                                    .findViewById(R.id.floating_toolbar_menu_item_image_button))
-                                    .setImageDrawable(menuItem.getIcon());
-                            menuButton.setMinimumWidth(mOverflowWidth);
-                            return menuButton;
-                        }
-                    };
-            overflowListView.setAdapter(overflowListViewAdapter);
-            return overflowListView;
-        }
-    }
-
-
-    /**
-     * A helper for fading in or out a view.
-     */
-    private static final class ViewFader {
-
-        private static final int FADE_OUT_DURATION = 250;
-        private static final int FADE_IN_DURATION = 150;
-
-        private final View mView;
-        private final ObjectAnimator mFadeOutAnimation;
-        private final ObjectAnimator mFadeInAnimation;
-
-        private ViewFader(View view) {
-            mView = Preconditions.checkNotNull(view);
-            mFadeOutAnimation = ObjectAnimator.ofFloat(view, View.ALPHA, 1, 0)
-                    .setDuration(FADE_OUT_DURATION);
-            mFadeInAnimation = ObjectAnimator.ofFloat(view, View.ALPHA, 0, 1)
-                    .setDuration(FADE_IN_DURATION);
-        }
-
-        public void fadeIn(boolean animate) {
-            cancelFadeAnimations();
-            if (animate) {
-                mFadeInAnimation.start();
-            } else {
-                mView.setAlpha(1);
+            public int getItemViewType(MenuItem menuItem) {
+                Preconditions.checkNotNull(menuItem);
+                if (isIconOnlyMenuItem(menuItem)) {
+                    return VIEW_TYPE_ICON_ONLY;
+                }
+                return VIEW_TYPE_STRING_TITLE;
             }
-        }
 
-        public void fadeOut(boolean animate) {
-            cancelFadeAnimations();
-            if (animate) {
-                mFadeOutAnimation.start();
-            } else {
-                mView.setAlpha(0);
+            public int calculateWidth(MenuItem menuItem) {
+                final View calculator;
+                if (isIconOnlyMenuItem(menuItem)) {
+                    ((ImageView) mIconOnlyViewCalculator
+                            .findViewById(R.id.floating_toolbar_menu_item_image_button))
+                            .setImageDrawable(menuItem.getIcon());
+                    calculator = mIconOnlyViewCalculator;
+                } else {
+                    mStringTitleViewCalculator.setText(menuItem.getTitle());
+                    calculator = mStringTitleViewCalculator;
+                }
+                calculator.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
+                return calculator.getMeasuredWidth();
             }
-        }
 
-        private void cancelFadeAnimations() {
-            mFadeInAnimation.cancel();
-            mFadeOutAnimation.cancel();
+            private TextView getStringTitleView(
+                    MenuItem menuItem, int minimumWidth, View convertView) {
+                TextView menuButton;
+                if (convertView != null) {
+                    menuButton = (TextView) convertView;
+                } else {
+                    menuButton = (TextView) LayoutInflater.from(mContext)
+                            .inflate(R.layout.floating_popup_overflow_list_item, null);
+                    menuButton.setLayoutParams(new ViewGroup.LayoutParams(
+                            ViewGroup.LayoutParams.MATCH_PARENT,
+                            ViewGroup.LayoutParams.WRAP_CONTENT));
+                }
+                if (menuItem != null) {
+                    menuButton.setText(menuItem.getTitle());
+                    menuButton.setContentDescription(menuItem.getTitle());
+                    menuButton.setMinimumWidth(minimumWidth);
+                }
+                return menuButton;
+            }
+
+            private View getIconOnlyView(
+                    MenuItem menuItem, int minimumWidth, View convertView) {
+                View menuButton;
+                if (convertView != null) {
+                    menuButton = convertView;
+                } else {
+                    menuButton = LayoutInflater.from(mContext).inflate(
+                            R.layout.floating_popup_overflow_image_list_item, null);
+                    menuButton.setLayoutParams(new ViewGroup.LayoutParams(
+                            ViewGroup.LayoutParams.WRAP_CONTENT,
+                            ViewGroup.LayoutParams.WRAP_CONTENT));
+                }
+                if (menuItem != null) {
+                    ((ImageView) menuButton
+                            .findViewById(R.id.floating_toolbar_menu_item_image_button))
+                            .setImageDrawable(menuItem.getIcon());
+                    menuButton.setMinimumWidth(minimumWidth);
+                }
+                return menuButton;
+            }
         }
     }
 
@@ -1453,14 +1596,6 @@
         return menuItemButton;
     }
 
-    /**
-     * Creates and returns a styled floating toolbar overflow list view item.
-     */
-    private static TextView createOverflowMenuItemButton(Context context) {
-        return (TextView) LayoutInflater.from(context)
-                .inflate(R.layout.floating_popup_overflow_list_item, null);
-    }
-
     private static ViewGroup createContentContainer(Context context) {
         ViewGroup contentContainer = (ViewGroup) LayoutInflater.from(context)
                 .inflate(R.layout.floating_popup_container, null);
@@ -1468,7 +1603,7 @@
         return contentContainer;
     }
 
-    private static PopupWindow createPopupWindow(View content) {
+    private static PopupWindow createPopupWindow(ViewGroup content) {
         ViewGroup popupContentHolder = new LinearLayout(content.getContext());
         PopupWindow popupWindow = new PopupWindow(popupContentHolder);
         // TODO: Use .setLayoutInScreenEnabled(true) instead of .setClippingEnabled(false)
@@ -1490,11 +1625,9 @@
      * @param view  The view to animate
      */
     private static AnimatorSet createEnterAnimation(View view) {
-        AnimatorSet animation =  new AnimatorSet();
+        AnimatorSet animation = new AnimatorSet();
         animation.playTogether(
-                ObjectAnimator.ofFloat(view, View.ALPHA, 0, 1).setDuration(150),
-                // Make sure that view.x is always fixed throughout the duration of this animation.
-                ObjectAnimator.ofFloat(view, View.X, view.getX(), view.getX()));
+                ObjectAnimator.ofFloat(view, View.ALPHA, 0, 1).setDuration(150));
         return animation;
     }
 
@@ -1525,13 +1658,4 @@
         a.recycle();
         return new ContextThemeWrapper(originalContext, themeId);
     }
-
-    private static int getEstimatedToolbarHeight(Context context) {
-        return context.getResources().getDimensionPixelSize(R.dimen.floating_toolbar_height);
-    }
-
-    private static int getEstimatedOpenOverflowButtonWidth(Context context) {
-        return context.getResources()
-                .getDimensionPixelSize(R.dimen.floating_toolbar_menu_button_minimum_width);
-    }
-}
+}
\ No newline at end of file
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 568b601..2940f6c 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1460,6 +1460,12 @@
         android:description="@string/permdesc_killBackgroundProcesses"
         android:protectionLevel="normal" />
 
+    <!-- @SystemApi @hide Allows an application to query process states and current
+         OOM adjustment scores.
+         <p>Not for use by third-party applications. -->
+    <permission android:name="android.permission.GET_PROCESS_STATE_AND_OOM_SCORE"
+        android:protectionLevel="signature|privileged|development" />
+
     <!-- @SystemApi @hide Allows an application to retrieve a package's importance.
          This permission is not available to third party applications. -->
     <permission android:name="android.permission.GET_PACKAGE_IMPORTANCE"
diff --git a/core/res/res/anim/ft_avd_toarrow_rectangle_1_animation.xml b/core/res/res/anim/ft_avd_toarrow_rectangle_1_animation.xml
new file mode 100644
index 0000000..e6a28bb
--- /dev/null
+++ b/core/res/res/anim/ft_avd_toarrow_rectangle_1_animation.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You 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.
+*/
+-->
+<set
+    xmlns:android="http://schemas.android.com/apk/res/android" >
+    <objectAnimator
+        android:duration="600"
+        android:propertyXName="translateX"
+        android:propertyYName="translateY"
+        android:pathData="M -6.5,0.0 c 1.08333,-1.0 5.41667,-5.0 6.5,-6.0"
+        android:interpolator="@interpolator/ft_avd_toarrow_animation_interpolator_4" />
+    <set
+        android:ordering="sequentially" >
+        <objectAnimator
+            android:duration="300"
+            android:propertyName="rotation"
+            android:valueFrom="-45.0"
+            android:valueTo="-45.0"
+            android:valueType="floatType"
+            android:interpolator="@android:interpolator/linear" />
+        <objectAnimator
+            android:duration="150"
+            android:propertyName="rotation"
+            android:valueFrom="-45.0"
+            android:valueTo="0.0"
+            android:valueType="floatType"
+            android:interpolator="@android:interpolator/linear" />
+    </set>
+</set>
diff --git a/core/res/res/anim/ft_avd_toarrow_rectangle_1_pivot_0_animation.xml b/core/res/res/anim/ft_avd_toarrow_rectangle_1_pivot_0_animation.xml
new file mode 100644
index 0000000..c2414de
--- /dev/null
+++ b/core/res/res/anim/ft_avd_toarrow_rectangle_1_pivot_0_animation.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You 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.
+*/
+-->
+<set
+    xmlns:android="http://schemas.android.com/apk/res/android" >
+    <objectAnimator
+        android:duration="200"
+        android:propertyXName="translateX"
+        android:propertyYName="translateY"
+        android:pathData="M 0.0,0.0 c 0.75,0.0 3.75,0.0 4.5,0.0"
+        android:interpolator="@interpolator/ft_avd_toarrow_animation_interpolator_5" />
+</set>
diff --git a/core/res/res/anim/ft_avd_toarrow_rectangle_1_pivot_animation.xml b/core/res/res/anim/ft_avd_toarrow_rectangle_1_pivot_animation.xml
new file mode 100644
index 0000000..593a0ea
--- /dev/null
+++ b/core/res/res/anim/ft_avd_toarrow_rectangle_1_pivot_animation.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You 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.
+*/
+-->
+<set
+    xmlns:android="http://schemas.android.com/apk/res/android" >
+    <objectAnimator
+        android:duration="450"
+        android:propertyXName="translateX"
+        android:propertyYName="translateY"
+        android:pathData="M 4.5,0.0 c -0.75,0.0 -3.75,0.0 -4.5,0.0"
+        android:interpolator="@interpolator/ft_avd_toarrow_animation_interpolator_0" />
+</set>
diff --git a/core/res/res/anim/ft_avd_toarrow_rectangle_2_animation.xml b/core/res/res/anim/ft_avd_toarrow_rectangle_2_animation.xml
new file mode 100644
index 0000000..5b09a41
--- /dev/null
+++ b/core/res/res/anim/ft_avd_toarrow_rectangle_2_animation.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You 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.
+*/
+-->
+<set
+    xmlns:android="http://schemas.android.com/apk/res/android" >
+    <objectAnimator
+        android:duration="600"
+        android:propertyXName="translateX"
+        android:propertyYName="translateY"
+        android:pathData="M -8.0,0.0 c 1.33333,0.0 6.66667,0.0 8.0,0.0"
+        android:interpolator="@interpolator/ft_avd_toarrow_animation_interpolator_3" />
+</set>
diff --git a/core/res/res/anim/ft_avd_toarrow_rectangle_2_pivot_0_animation.xml b/core/res/res/anim/ft_avd_toarrow_rectangle_2_pivot_0_animation.xml
new file mode 100644
index 0000000..dccbc67
--- /dev/null
+++ b/core/res/res/anim/ft_avd_toarrow_rectangle_2_pivot_0_animation.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You 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.
+*/
+-->
+<set
+    xmlns:android="http://schemas.android.com/apk/res/android" >
+    <objectAnimator
+        android:duration="250"
+        android:propertyXName="translateX"
+        android:propertyYName="translateY"
+        android:pathData="M 0.0,0.0 c 1.66667,0.0 8.5,0.0 10.0,0.0 c 0.13052,0.0 -0.83333,0.0 -1.0,0.0"
+        android:interpolator="@interpolator/ft_avd_toarrow_animation_interpolator_2" />
+</set>
diff --git a/core/res/res/anim/ft_avd_toarrow_rectangle_2_pivot_animation.xml b/core/res/res/anim/ft_avd_toarrow_rectangle_2_pivot_animation.xml
new file mode 100644
index 0000000..c1c0c1c
--- /dev/null
+++ b/core/res/res/anim/ft_avd_toarrow_rectangle_2_pivot_animation.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You 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.
+*/
+-->
+<set
+    xmlns:android="http://schemas.android.com/apk/res/android" >
+    <objectAnimator
+        android:duration="516"
+        android:propertyXName="translateX"
+        android:propertyYName="translateY"
+        android:pathData="M 9.0,0.0 c -1.5,0.0 -7.5,0.0 -9.0,0.0"
+        android:interpolator="@interpolator/ft_avd_toarrow_animation_interpolator_6" />
+</set>
diff --git a/core/res/res/anim/ft_avd_toarrow_rectangle_3_animation.xml b/core/res/res/anim/ft_avd_toarrow_rectangle_3_animation.xml
new file mode 100644
index 0000000..df69bf7
--- /dev/null
+++ b/core/res/res/anim/ft_avd_toarrow_rectangle_3_animation.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You 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.
+*/
+-->
+<set
+    xmlns:android="http://schemas.android.com/apk/res/android" >
+    <objectAnimator
+        android:duration="600"
+        android:propertyXName="translateX"
+        android:propertyYName="translateY"
+        android:pathData="M -6.5,0.0 c 1.08333,1.0 5.41667,5.0 6.5,6.0"
+        android:interpolator="@interpolator/ft_avd_toarrow_animation_interpolator_4" />
+    <set
+        android:ordering="sequentially" >
+        <objectAnimator
+            android:duration="300"
+            android:propertyName="rotation"
+            android:valueFrom="45.0"
+            android:valueTo="45.0"
+            android:valueType="floatType"
+            android:interpolator="@android:interpolator/linear" />
+        <objectAnimator
+            android:duration="150"
+            android:propertyName="rotation"
+            android:valueFrom="45.0"
+            android:valueTo="0.0"
+            android:valueType="floatType"
+            android:interpolator="@android:interpolator/linear" />
+    </set>
+</set>
diff --git a/core/res/res/anim/ft_avd_toarrow_rectangle_3_pivot_0_animation.xml b/core/res/res/anim/ft_avd_toarrow_rectangle_3_pivot_0_animation.xml
new file mode 100644
index 0000000..c2414de
--- /dev/null
+++ b/core/res/res/anim/ft_avd_toarrow_rectangle_3_pivot_0_animation.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You 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.
+*/
+-->
+<set
+    xmlns:android="http://schemas.android.com/apk/res/android" >
+    <objectAnimator
+        android:duration="200"
+        android:propertyXName="translateX"
+        android:propertyYName="translateY"
+        android:pathData="M 0.0,0.0 c 0.75,0.0 3.75,0.0 4.5,0.0"
+        android:interpolator="@interpolator/ft_avd_toarrow_animation_interpolator_5" />
+</set>
diff --git a/core/res/res/anim/ft_avd_toarrow_rectangle_3_pivot_animation.xml b/core/res/res/anim/ft_avd_toarrow_rectangle_3_pivot_animation.xml
new file mode 100644
index 0000000..593a0ea
--- /dev/null
+++ b/core/res/res/anim/ft_avd_toarrow_rectangle_3_pivot_animation.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You 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.
+*/
+-->
+<set
+    xmlns:android="http://schemas.android.com/apk/res/android" >
+    <objectAnimator
+        android:duration="450"
+        android:propertyXName="translateX"
+        android:propertyYName="translateY"
+        android:pathData="M 4.5,0.0 c -0.75,0.0 -3.75,0.0 -4.5,0.0"
+        android:interpolator="@interpolator/ft_avd_toarrow_animation_interpolator_0" />
+</set>
diff --git a/core/res/res/anim/ft_avd_toarrow_rectangle_4_animation.xml b/core/res/res/anim/ft_avd_toarrow_rectangle_4_animation.xml
new file mode 100644
index 0000000..a375330
--- /dev/null
+++ b/core/res/res/anim/ft_avd_toarrow_rectangle_4_animation.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You 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.
+*/
+-->
+<set
+    xmlns:android="http://schemas.android.com/apk/res/android" >
+    <objectAnimator
+        android:duration="300"
+        android:propertyXName="translateX"
+        android:propertyYName="translateY"
+        android:pathData="M 0.0,6.0 c -1.08333,-1.0 -5.41667,-5.0 -6.5,-6.0"
+        android:interpolator="@android:interpolator/fast_out_slow_in" />
+    <objectAnimator
+        android:duration="400"
+        android:propertyName="rotation"
+        android:valueFrom="0.0"
+        android:valueTo="45.0"
+        android:valueType="floatType"
+        android:interpolator="@interpolator/ft_avd_toarrow_animation_interpolator_1" />
+</set>
diff --git a/core/res/res/anim/ft_avd_toarrow_rectangle_5_animation.xml b/core/res/res/anim/ft_avd_toarrow_rectangle_5_animation.xml
new file mode 100644
index 0000000..ae7ee47
--- /dev/null
+++ b/core/res/res/anim/ft_avd_toarrow_rectangle_5_animation.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You 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.
+*/
+-->
+<set
+    xmlns:android="http://schemas.android.com/apk/res/android" >
+    <objectAnimator
+        android:duration="250"
+        android:propertyXName="translateX"
+        android:propertyYName="translateY"
+        android:pathData="M 0.0,0.0 c -1.33333,0.0 -6.66667,0.0 -8.0,0.0"
+        android:interpolator="@android:interpolator/fast_out_slow_in" />
+</set>
diff --git a/core/res/res/anim/ft_avd_toarrow_rectangle_6_animation.xml b/core/res/res/anim/ft_avd_toarrow_rectangle_6_animation.xml
new file mode 100644
index 0000000..3b872af
--- /dev/null
+++ b/core/res/res/anim/ft_avd_toarrow_rectangle_6_animation.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You 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.
+*/
+-->
+<set
+    xmlns:android="http://schemas.android.com/apk/res/android" >
+    <objectAnimator
+        android:duration="300"
+        android:propertyXName="translateX"
+        android:propertyYName="translateY"
+        android:pathData="M 0.0,-6.0 c -1.08333,1.0 -5.41667,5.0 -6.5,6.0"
+        android:interpolator="@android:interpolator/fast_out_slow_in" />
+    <objectAnimator
+        android:duration="400"
+        android:propertyName="rotation"
+        android:valueFrom="0.0"
+        android:valueTo="-45.0"
+        android:valueType="floatType"
+        android:interpolator="@interpolator/ft_avd_toarrow_animation_interpolator_1" />
+</set>
diff --git a/core/res/res/anim/ft_avd_toarrow_rectangle_path_1_animation.xml b/core/res/res/anim/ft_avd_toarrow_rectangle_path_1_animation.xml
new file mode 100644
index 0000000..94e04b4
--- /dev/null
+++ b/core/res/res/anim/ft_avd_toarrow_rectangle_path_1_animation.xml
@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You 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.
+*/
+-->
+<set
+    xmlns:android="http://schemas.android.com/apk/res/android" >
+    <set
+        android:ordering="sequentially" >
+        <objectAnimator
+            android:duration="300"
+            android:propertyName="pathData"
+            android:valueFrom="M -5.5,-1.0 l 11.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l -11.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z"
+            android:valueTo="M -5.5,-1.0 l 11.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l -11.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z"
+            android:valueType="pathType"
+            android:interpolator="@android:interpolator/linear" />
+        <objectAnimator
+            android:duration="37"
+            android:propertyName="pathData"
+            android:valueFrom="M -5.5,-1.0 l 11.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l -11.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z"
+            android:valueTo="M -4.54266574364,-1.0 l 9.08533148727,0.0 c 0.528721110251,0.0 0.957334256364,0.428613146113 0.957334256364,0.957334256364 l 0.0,0.0853314872718 c 0.0,0.528721110251 -0.428613146113,0.957334256364 -0.957334256364,0.957334256364 l -9.08533148727,0.0 c -0.528721110251,0.0 -0.957334256364,-0.428613146113 -0.957334256364,-0.957334256364 l 0.0,-0.0853314872718 c 0.0,-0.528721110251 0.428613146113,-0.957334256364 0.957334256364,-0.957334256364 Z"
+            android:valueType="pathType"
+            android:interpolator="@android:interpolator/linear" />
+        <objectAnimator
+            android:duration="37"
+            android:propertyName="pathData"
+            android:valueFrom="M -4.54266574364,-1.0 l 9.08533148727,0.0 c 0.528721110251,0.0 0.957334256364,0.428613146113 0.957334256364,0.957334256364 l 0.0,0.0853314872718 c 0.0,0.528721110251 -0.428613146113,0.957334256364 -0.957334256364,0.957334256364 l -9.08533148727,0.0 c -0.528721110251,0.0 -0.957334256364,-0.428613146113 -0.957334256364,-0.957334256364 l 0.0,-0.0853314872718 c 0.0,-0.528721110251 0.428613146113,-0.957334256364 0.957334256364,-0.957334256364 Z"
+            android:valueTo="M -3.54375000003,-1.21249999999 l 7.08750000005,0.0 c 0.669645259129,0.0 1.21249999999,0.542854740865 1.21249999999,1.21249999999 l 0.0,0.0 c 0.0,0.669645259129 -0.542854740865,1.21249999999 -1.21249999999,1.21249999999 l -7.08750000005,0.0 c -0.669645259129,0.0 -1.21249999999,-0.542854740865 -1.21249999999,-1.21249999999 l 0.0,0.0 c 0.0,-0.669645259129 0.542854740865,-1.21249999999 1.21249999999,-1.21249999999 Z"
+            android:valueType="pathType"
+            android:interpolator="@android:interpolator/linear" />
+        <objectAnimator
+            android:duration="37"
+            android:propertyName="pathData"
+            android:valueFrom="M -3.54375000003,-1.21249999999 l 7.08750000005,0.0 c 0.669645259129,0.0 1.21249999999,0.542854740865 1.21249999999,1.21249999999 l 0.0,0.0 c 0.0,0.669645259129 -0.542854740865,1.21249999999 -1.21249999999,1.21249999999 l -7.08750000005,0.0 c -0.669645259129,0.0 -1.21249999999,-0.542854740865 -1.21249999999,-1.21249999999 l 0.0,0.0 c 0.0,-0.669645259129 0.542854740865,-1.21249999999 1.21249999999,-1.21249999999 Z"
+            android:valueTo="M -1.68627963028,-1.62527119327 l 3.37255926056,0.0 c 0.897612494333,0.0 1.62527119327,0.727658698938 1.62527119327,1.62527119327 l 0.0,0.0 c 0.0,0.897612494333 -0.727658698938,1.62527119327 -1.62527119327,1.62527119327 l -3.37255926056,0.0 c -0.897612494333,0.0 -1.62527119327,-0.727658698938 -1.62527119327,-1.62527119327 l 0.0,0.0 c 0.0,-0.897612494333 0.727658698938,-1.62527119327 1.62527119327,-1.62527119327 Z"
+            android:valueType="pathType"
+            android:interpolator="@android:interpolator/linear" />
+        <objectAnimator
+            android:duration="37"
+            android:propertyName="pathData"
+            android:valueFrom="M -1.68627963028,-1.62527119327 l 3.37255926056,0.0 c 0.897612494333,0.0 1.62527119327,0.727658698938 1.62527119327,1.62527119327 l 0.0,0.0 c 0.0,0.897612494333 -0.727658698938,1.62527119327 -1.62527119327,1.62527119327 l -3.37255926056,0.0 c -0.897612494333,0.0 -1.62527119327,-0.727658698938 -1.62527119327,-1.62527119327 l 0.0,0.0 c 0.0,-0.897612494333 0.727658698938,-1.62527119327 1.62527119327,-1.62527119327 Z"
+            android:valueTo="M -0.866611597071,-1.8074196451 l 1.73322319414,0.0 c 0.998210306475,0.0 1.8074196451,0.80920933862 1.8074196451,1.8074196451 l 0.0,0.0 c 0.0,0.998210306475 -0.80920933862,1.8074196451 -1.8074196451,1.8074196451 l -1.73322319414,0.0 c -0.998210306475,0.0 -1.8074196451,-0.80920933862 -1.8074196451,-1.8074196451 l 0.0,0.0 c 0.0,-0.998210306475 0.80920933862,-1.8074196451 1.8074196451,-1.8074196451 Z"
+            android:valueType="pathType"
+            android:interpolator="@android:interpolator/linear" />
+        <objectAnimator
+            android:duration="37"
+            android:propertyName="pathData"
+            android:valueFrom="M -0.866611597071,-1.8074196451 l 1.73322319414,0.0 c 0.998210306475,0.0 1.8074196451,0.80920933862 1.8074196451,1.8074196451 l 0.0,0.0 c 0.0,0.998210306475 -0.80920933862,1.8074196451 -1.8074196451,1.8074196451 l -1.73322319414,0.0 c -0.998210306475,0.0 -1.8074196451,-0.80920933862 -1.8074196451,-1.8074196451 l 0.0,0.0 c 0.0,-0.998210306475 0.80920933862,-1.8074196451 1.8074196451,-1.8074196451 Z"
+            android:valueTo="M -0.416762258226,-1.90738616484 l 0.833524516453,0.0 c 1.05342029082,0.0 1.90738616484,0.853965874019 1.90738616484,1.90738616484 l 0.0,0.0 c 0.0,1.05342029082 -0.853965874019,1.90738616484 -1.90738616484,1.90738616484 l -0.833524516453,0.0 c -1.05342029082,0.0 -1.90738616484,-0.853965874019 -1.90738616484,-1.90738616484 l 0.0,0.0 c 0.0,-1.05342029082 0.853965874019,-1.90738616484 1.90738616484,-1.90738616484 Z"
+            android:valueType="pathType"
+            android:interpolator="@android:interpolator/linear" />
+        <objectAnimator
+            android:duration="37"
+            android:propertyName="pathData"
+            android:valueFrom="M -0.416762258226,-1.90738616484 l 0.833524516453,0.0 c 1.05342029082,0.0 1.90738616484,0.853965874019 1.90738616484,1.90738616484 l 0.0,0.0 c 0.0,1.05342029082 -0.853965874019,1.90738616484 -1.90738616484,1.90738616484 l -0.833524516453,0.0 c -1.05342029082,0.0 -1.90738616484,-0.853965874019 -1.90738616484,-1.90738616484 l 0.0,0.0 c 0.0,-1.05342029082 0.853965874019,-1.90738616484 1.90738616484,-1.90738616484 Z"
+            android:valueTo="M -0.163643891635,-1.96363469075 l 0.32728778327,0.0 c 1.08448549388,0.0 1.96363469075,0.87914919687 1.96363469075,1.96363469075 l 0.0,0.0 c 0.0,1.08448549388 -0.87914919687,1.96363469075 -1.96363469075,1.96363469075 l -0.32728778327,0.0 c -1.08448549388,0.0 -1.96363469075,-0.87914919687 -1.96363469075,-1.96363469075 l 0.0,0.0 c 0.0,-1.08448549388 0.87914919687,-1.96363469075 1.96363469075,-1.96363469075 Z"
+            android:valueType="pathType"
+            android:interpolator="@android:interpolator/linear" />
+        <objectAnimator
+            android:duration="37"
+            android:propertyName="pathData"
+            android:valueFrom="M -0.163643891635,-1.96363469075 l 0.32728778327,0.0 c 1.08448549388,0.0 1.96363469075,0.87914919687 1.96363469075,1.96363469075 l 0.0,0.0 c 0.0,1.08448549388 -0.87914919687,1.96363469075 -1.96363469075,1.96363469075 l -0.32728778327,0.0 c -1.08448549388,0.0 -1.96363469075,-0.87914919687 -1.96363469075,-1.96363469075 l 0.0,0.0 c 0.0,-1.08448549388 0.87914919687,-1.96363469075 1.96363469075,-1.96363469075 Z"
+            android:valueTo="M -0.0368976091105,-1.99180053131 l 0.073795218221,0.0 c 1.10004105809,0.0 1.99180053131,0.891759473223 1.99180053131,1.99180053131 l 0.0,0.0 c 0.0,1.10004105809 -0.891759473223,1.99180053131 -1.99180053131,1.99180053131 l -0.073795218221,0.0 c -1.10004105809,0.0 -1.99180053131,-0.891759473223 -1.99180053131,-1.99180053131 l 0.0,0.0 c 0.0,-1.10004105809 0.891759473223,-1.99180053131 1.99180053131,-1.99180053131 Z"
+            android:valueType="pathType"
+            android:interpolator="@android:interpolator/linear" />
+        <objectAnimator
+            android:duration="37"
+            android:propertyName="pathData"
+            android:valueFrom="M -0.0368976091105,-1.99180053131 l 0.073795218221,0.0 c 1.10004105809,0.0 1.99180053131,0.891759473223 1.99180053131,1.99180053131 l 0.0,0.0 c 0.0,1.10004105809 -0.891759473223,1.99180053131 -1.99180053131,1.99180053131 l -0.073795218221,0.0 c -1.10004105809,0.0 -1.99180053131,-0.891759473223 -1.99180053131,-1.99180053131 l 0.0,0.0 c 0.0,-1.10004105809 0.891759473223,-1.99180053131 1.99180053131,-1.99180053131 Z"
+            android:valueTo="M 0.0,-2.0 l 0.0,0.0 c 1.1045694996,0.0 2.0,0.8954305004 2.0,2.0 l 0.0,0.0 c 0.0,1.1045694996 -0.8954305004,2.0 -2.0,2.0 l 0.0,0.0 c -1.1045694996,0.0 -2.0,-0.8954305004 -2.0,-2.0 l 0.0,0.0 c 0.0,-1.1045694996 0.8954305004,-2.0 2.0,-2.0 Z"
+            android:valueType="pathType"
+            android:interpolator="@android:interpolator/linear" />
+    </set>
+</set>
diff --git a/core/res/res/anim/ft_avd_toarrow_rectangle_path_2_animation.xml b/core/res/res/anim/ft_avd_toarrow_rectangle_path_2_animation.xml
new file mode 100644
index 0000000..423f8d7
--- /dev/null
+++ b/core/res/res/anim/ft_avd_toarrow_rectangle_path_2_animation.xml
@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You 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.
+*/
+-->
+<set
+    xmlns:android="http://schemas.android.com/apk/res/android" >
+    <set
+        android:ordering="sequentially" >
+        <objectAnimator
+            android:duration="300"
+            android:propertyName="pathData"
+            android:valueFrom="M -5.5,-1.0 l 11.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l -11.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z"
+            android:valueTo="M -5.5,-1.0 l 11.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l -11.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z"
+            android:valueType="pathType"
+            android:interpolator="@android:interpolator/linear" />
+        <objectAnimator
+            android:duration="37"
+            android:propertyName="pathData"
+            android:valueFrom="M -5.5,-1.0 l 11.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l -11.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z"
+            android:valueTo="M -4.54266574364,-1.0 l 9.08533148727,0.0 c 0.528721110251,0.0 0.957334256364,0.428613146113 0.957334256364,0.957334256364 l 0.0,0.0853314872718 c 0.0,0.528721110251 -0.428613146113,0.957334256364 -0.957334256364,0.957334256364 l -9.08533148727,0.0 c -0.528721110251,0.0 -0.957334256364,-0.428613146113 -0.957334256364,-0.957334256364 l 0.0,-0.0853314872718 c 0.0,-0.528721110251 0.428613146113,-0.957334256364 0.957334256364,-0.957334256364 Z"
+            android:valueType="pathType"
+            android:interpolator="@android:interpolator/linear" />
+        <objectAnimator
+            android:duration="37"
+            android:propertyName="pathData"
+            android:valueFrom="M -4.54266574364,-1.0 l 9.08533148727,0.0 c 0.528721110251,0.0 0.957334256364,0.428613146113 0.957334256364,0.957334256364 l 0.0,0.0853314872718 c 0.0,0.528721110251 -0.428613146113,0.957334256364 -0.957334256364,0.957334256364 l -9.08533148727,0.0 c -0.528721110251,0.0 -0.957334256364,-0.428613146113 -0.957334256364,-0.957334256364 l 0.0,-0.0853314872718 c 0.0,-0.528721110251 0.428613146113,-0.957334256364 0.957334256364,-0.957334256364 Z"
+            android:valueTo="M -2.49262086719,-1.44608425174 l 4.98524173437,0.0 c 0.79865027916,0.0 1.44608425174,0.647433972576 1.44608425174,1.44608425174 l 0.0,0.0 c 0.0,0.79865027916 -0.647433972576,1.44608425174 -1.44608425174,1.44608425174 l -4.98524173437,0.0 c -0.79865027916,0.0 -1.44608425174,-0.647433972576 -1.44608425174,-1.44608425174 l 0.0,0.0 c 0.0,-0.79865027916 0.647433972576,-1.44608425174 1.44608425174,-1.44608425174 Z"
+            android:valueType="pathType"
+            android:interpolator="@android:interpolator/linear" />
+        <objectAnimator
+            android:duration="37"
+            android:propertyName="pathData"
+            android:valueFrom="M -2.49262086719,-1.44608425174 l 4.98524173437,0.0 c 0.79865027916,0.0 1.44608425174,0.647433972576 1.44608425174,1.44608425174 l 0.0,0.0 c 0.0,0.79865027916 -0.647433972576,1.44608425174 -1.44608425174,1.44608425174 l -4.98524173437,0.0 c -0.79865027916,0.0 -1.44608425174,-0.647433972576 -1.44608425174,-1.44608425174 l 0.0,0.0 c 0.0,-0.79865027916 0.647433972576,-1.44608425174 1.44608425174,-1.44608425174 Z"
+            android:valueTo="M -1.39285008918,-1.69047775796 l 2.78570017836,0.0 c 0.933625085597,0.0 1.69047775796,0.756852672362 1.69047775796,1.69047775796 l 0.0,0.0 c 0.0,0.933625085597 -0.756852672362,1.69047775796 -1.69047775796,1.69047775796 l -2.78570017836,0.0 c -0.933625085597,0.0 -1.69047775796,-0.756852672362 -1.69047775796,-1.69047775796 l 0.0,0.0 c 0.0,-0.933625085597 0.756852672362,-1.69047775796 1.69047775796,-1.69047775796 Z"
+            android:valueType="pathType"
+            android:interpolator="@android:interpolator/linear" />
+        <objectAnimator
+            android:duration="37"
+            android:propertyName="pathData"
+            android:valueFrom="M -1.39285008918,-1.69047775796 l 2.78570017836,0.0 c 0.933625085597,0.0 1.69047775796,0.756852672362 1.69047775796,1.69047775796 l 0.0,0.0 c 0.0,0.933625085597 -0.756852672362,1.69047775796 -1.69047775796,1.69047775796 l -2.78570017836,0.0 c -0.933625085597,0.0 -1.69047775796,-0.756852672362 -1.69047775796,-1.69047775796 l 0.0,0.0 c 0.0,-0.933625085597 0.756852672362,-1.69047775796 1.69047775796,-1.69047775796 Z"
+            android:valueTo="M -0.77117021921,-1.82862884018 l 1.54234043842,0.0 c 1.00992382147,0.0 1.82862884018,0.818705018702 1.82862884018,1.82862884018 l 0.0,0.0 c 0.0,1.00992382147 -0.818705018702,1.82862884018 -1.82862884018,1.82862884018 l -1.54234043842,0.0 c -1.00992382147,0.0 -1.82862884018,-0.818705018702 -1.82862884018,-1.82862884018 l 0.0,0.0 c 0.0,-1.00992382147 0.818705018702,-1.82862884018 1.82862884018,-1.82862884018 Z"
+            android:valueType="pathType"
+            android:interpolator="@android:interpolator/linear" />
+        <objectAnimator
+            android:duration="37"
+            android:propertyName="pathData"
+            android:valueFrom="M -0.77117021921,-1.82862884018 l 1.54234043842,0.0 c 1.00992382147,0.0 1.82862884018,0.818705018702 1.82862884018,1.82862884018 l 0.0,0.0 c 0.0,1.00992382147 -0.818705018702,1.82862884018 -1.82862884018,1.82862884018 l -1.54234043842,0.0 c -1.00992382147,0.0 -1.82862884018,-0.818705018702 -1.82862884018,-1.82862884018 l 0.0,0.0 c 0.0,-1.00992382147 0.818705018702,-1.82862884018 1.82862884018,-1.82862884018 Z"
+            android:valueTo="M -0.387649645988,-1.91385563422 l 0.775299291975,0.0 c 1.0569932801,0.0 1.91385563422,0.856862354124 1.91385563422,1.91385563422 l 0.0,0.0 c 0.0,1.0569932801 -0.856862354124,1.91385563422 -1.91385563422,1.91385563422 l -0.775299291975,0.0 c -1.0569932801,0.0 -1.91385563422,-0.856862354124 -1.91385563422,-1.91385563422 l 0.0,0.0 c 0.0,-1.0569932801 0.856862354124,-1.91385563422 1.91385563422,-1.91385563422 Z"
+            android:valueType="pathType"
+            android:interpolator="@android:interpolator/linear" />
+        <objectAnimator
+            android:duration="37"
+            android:propertyName="pathData"
+            android:valueFrom="M -0.387649645988,-1.91385563422 l 0.775299291975,0.0 c 1.0569932801,0.0 1.91385563422,0.856862354124 1.91385563422,1.91385563422 l 0.0,0.0 c 0.0,1.0569932801 -0.856862354124,1.91385563422 -1.91385563422,1.91385563422 l -0.775299291975,0.0 c -1.0569932801,0.0 -1.91385563422,-0.856862354124 -1.91385563422,-1.91385563422 l 0.0,0.0 c 0.0,-1.0569932801 0.856862354124,-1.91385563422 1.91385563422,-1.91385563422 Z"
+            android:valueTo="M -0.156963485208,-1.96511922551 l 0.313926970417,0.0 c 1.08530537979,0.0 1.96511922551,0.879813845722 1.96511922551,1.96511922551 l 0.0,0.0 c 0.0,1.08530537979 -0.879813845722,1.96511922551 -1.96511922551,1.96511922551 l -0.313926970417,0.0 c -1.08530537979,0.0 -1.96511922551,-0.879813845722 -1.96511922551,-1.96511922551 l 0.0,0.0 c 0.0,-1.08530537979 0.879813845722,-1.96511922551 1.96511922551,-1.96511922551 Z"
+            android:valueType="pathType"
+            android:interpolator="@android:interpolator/linear" />
+        <objectAnimator
+            android:duration="37"
+            android:propertyName="pathData"
+            android:valueFrom="M -0.156963485208,-1.96511922551 l 0.313926970417,0.0 c 1.08530537979,0.0 1.96511922551,0.879813845722 1.96511922551,1.96511922551 l 0.0,0.0 c 0.0,1.08530537979 -0.879813845722,1.96511922551 -1.96511922551,1.96511922551 l -0.313926970417,0.0 c -1.08530537979,0.0 -1.96511922551,-0.879813845722 -1.96511922551,-1.96511922551 l 0.0,0.0 c 0.0,-1.08530537979 0.879813845722,-1.96511922551 1.96511922551,-1.96511922551 Z"
+            android:valueTo="M -0.0362224951386,-1.99195055664 l 0.0724449902773,0.0 c 1.10012391479,0.0 1.99195055664,0.89182664185 1.99195055664,1.99195055664 l 0.0,0.0 c 0.0,1.10012391479 -0.89182664185,1.99195055664 -1.99195055664,1.99195055664 l -0.0724449902773,0.0 c -1.10012391479,0.0 -1.99195055664,-0.89182664185 -1.99195055664,-1.99195055664 l 0.0,0.0 c 0.0,-1.10012391479 0.89182664185,-1.99195055664 1.99195055664,-1.99195055664 Z"
+            android:valueType="pathType"
+            android:interpolator="@android:interpolator/linear" />
+        <objectAnimator
+            android:duration="37"
+            android:propertyName="pathData"
+            android:valueFrom="M -0.0362224951386,-1.99195055664 l 0.0724449902773,0.0 c 1.10012391479,0.0 1.99195055664,0.89182664185 1.99195055664,1.99195055664 l 0.0,0.0 c 0.0,1.10012391479 -0.89182664185,1.99195055664 -1.99195055664,1.99195055664 l -0.0724449902773,0.0 c -1.10012391479,0.0 -1.99195055664,-0.89182664185 -1.99195055664,-1.99195055664 l 0.0,0.0 c 0.0,-1.10012391479 0.89182664185,-1.99195055664 1.99195055664,-1.99195055664 Z"
+            android:valueTo="M 0.0,-2.0 l 0.0,0.0 c 1.1045694996,0.0 2.0,0.8954305004 2.0,2.0 l 0.0,0.0 c 0.0,1.1045694996 -0.8954305004,2.0 -2.0,2.0 l 0.0,0.0 c -1.1045694996,0.0 -2.0,-0.8954305004 -2.0,-2.0 l 0.0,0.0 c 0.0,-1.1045694996 0.8954305004,-2.0 2.0,-2.0 Z"
+            android:valueType="pathType"
+            android:interpolator="@android:interpolator/linear" />
+    </set>
+</set>
diff --git a/core/res/res/anim/ft_avd_toarrow_rectangle_path_3_animation.xml b/core/res/res/anim/ft_avd_toarrow_rectangle_path_3_animation.xml
new file mode 100644
index 0000000..444f6b6
--- /dev/null
+++ b/core/res/res/anim/ft_avd_toarrow_rectangle_path_3_animation.xml
@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You 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.
+*/
+-->
+<set
+    xmlns:android="http://schemas.android.com/apk/res/android" >
+    <set
+        android:ordering="sequentially" >
+        <objectAnimator
+            android:duration="300"
+            android:propertyName="pathData"
+            android:valueFrom="M -7.0,-1.0 l 14.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l -14.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z"
+            android:valueTo="M -7.0,-1.0 l 14.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l -14.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z"
+            android:valueType="pathType"
+            android:interpolator="@android:interpolator/linear" />
+        <objectAnimator
+            android:duration="37"
+            android:propertyName="pathData"
+            android:valueFrom="M -7.0,-1.0 l 14.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l -14.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z"
+            android:valueTo="M -6.04266574364,-1.0 l 12.0853314873,0.0 c 0.528721110251,0.0 0.957334256364,0.428613146113 0.957334256364,0.957334256364 l 0.0,0.0853314872718 c 0.0,0.528721110251 -0.428613146113,0.957334256364 -0.957334256364,0.957334256364 l -12.0853314873,0.0 c -0.528721110251,0.0 -0.957334256364,-0.428613146113 -0.957334256364,-0.957334256364 l 0.0,-0.0853314872718 c 0.0,-0.528721110251 0.428613146113,-0.957334256364 0.957334256364,-0.957334256364 Z"
+            android:valueType="pathType"
+            android:interpolator="@android:interpolator/linear" />
+        <objectAnimator
+            android:duration="37"
+            android:propertyName="pathData"
+            android:valueFrom="M -6.04266574364,-1.0 l 12.0853314873,0.0 c 0.528721110251,0.0 0.957334256364,0.428613146113 0.957334256364,0.957334256364 l 0.0,0.0853314872718 c 0.0,0.528721110251 -0.428613146113,0.957334256364 -0.957334256364,0.957334256364 l -12.0853314873,0.0 c -0.528721110251,0.0 -0.957334256364,-0.428613146113 -0.957334256364,-0.957334256364 l 0.0,-0.0853314872718 c 0.0,-0.528721110251 0.428613146113,-0.957334256364 0.957334256364,-0.957334256364 Z"
+            android:valueTo="M -3.32349448958,-1.44608425174 l 6.64698897916,0.0 c 0.79865027916,0.0 1.44608425174,0.647433972576 1.44608425174,1.44608425174 l 0.0,0.0 c 0.0,0.79865027916 -0.647433972576,1.44608425174 -1.44608425174,1.44608425174 l -6.64698897916,0.0 c -0.79865027916,0.0 -1.44608425174,-0.647433972576 -1.44608425174,-1.44608425174 l 0.0,0.0 c 0.0,-0.79865027916 0.647433972576,-1.44608425174 1.44608425174,-1.44608425174 Z"
+            android:valueType="pathType"
+            android:interpolator="@android:interpolator/linear" />
+        <objectAnimator
+            android:duration="37"
+            android:propertyName="pathData"
+            android:valueFrom="M -3.32349448958,-1.44608425174 l 6.64698897916,0.0 c 0.79865027916,0.0 1.44608425174,0.647433972576 1.44608425174,1.44608425174 l 0.0,0.0 c 0.0,0.79865027916 -0.647433972576,1.44608425174 -1.44608425174,1.44608425174 l -6.64698897916,0.0 c -0.79865027916,0.0 -1.44608425174,-0.647433972576 -1.44608425174,-1.44608425174 l 0.0,0.0 c 0.0,-0.79865027916 0.647433972576,-1.44608425174 1.44608425174,-1.44608425174 Z"
+            android:valueTo="M -1.85713345224,-1.69047775796 l 3.71426690449,0.0 c 0.933625085597,0.0 1.69047775796,0.756852672362 1.69047775796,1.69047775796 l 0.0,0.0 c 0.0,0.933625085597 -0.756852672362,1.69047775796 -1.69047775796,1.69047775796 l -3.71426690449,0.0 c -0.933625085597,0.0 -1.69047775796,-0.756852672362 -1.69047775796,-1.69047775796 l 0.0,0.0 c 0.0,-0.933625085597 0.756852672362,-1.69047775796 1.69047775796,-1.69047775796 Z"
+            android:valueType="pathType"
+            android:interpolator="@android:interpolator/linear" />
+        <objectAnimator
+            android:duration="37"
+            android:propertyName="pathData"
+            android:valueFrom="M -1.85713345224,-1.69047775796 l 3.71426690449,0.0 c 0.933625085597,0.0 1.69047775796,0.756852672362 1.69047775796,1.69047775796 l 0.0,0.0 c 0.0,0.933625085597 -0.756852672362,1.69047775796 -1.69047775796,1.69047775796 l -3.71426690449,0.0 c -0.933625085597,0.0 -1.69047775796,-0.756852672362 -1.69047775796,-1.69047775796 l 0.0,0.0 c 0.0,-0.933625085597 0.756852672362,-1.69047775796 1.69047775796,-1.69047775796 Z"
+            android:valueTo="M -1.02822695895,-1.82862884018 l 2.05645391789,0.0 c 1.00992382147,0.0 1.82862884018,0.818705018702 1.82862884018,1.82862884018 l 0.0,0.0 c 0.0,1.00992382147 -0.818705018702,1.82862884018 -1.82862884018,1.82862884018 l -2.05645391789,0.0 c -1.00992382147,0.0 -1.82862884018,-0.818705018702 -1.82862884018,-1.82862884018 l 0.0,0.0 c 0.0,-1.00992382147 0.818705018702,-1.82862884018 1.82862884018,-1.82862884018 Z"
+            android:valueType="pathType"
+            android:interpolator="@android:interpolator/linear" />
+        <objectAnimator
+            android:duration="37"
+            android:propertyName="pathData"
+            android:valueFrom="M -1.02822695895,-1.82862884018 l 2.05645391789,0.0 c 1.00992382147,0.0 1.82862884018,0.818705018702 1.82862884018,1.82862884018 l 0.0,0.0 c 0.0,1.00992382147 -0.818705018702,1.82862884018 -1.82862884018,1.82862884018 l -2.05645391789,0.0 c -1.00992382147,0.0 -1.82862884018,-0.818705018702 -1.82862884018,-1.82862884018 l 0.0,0.0 c 0.0,-1.00992382147 0.818705018702,-1.82862884018 1.82862884018,-1.82862884018 Z"
+            android:valueTo="M -0.51686619465,-1.91385563422 l 1.0337323893,0.0 c 1.0569932801,0.0 1.91385563422,0.856862354124 1.91385563422,1.91385563422 l 0.0,0.0 c 0.0,1.0569932801 -0.856862354124,1.91385563422 -1.91385563422,1.91385563422 l -1.0337323893,0.0 c -1.0569932801,0.0 -1.91385563422,-0.856862354124 -1.91385563422,-1.91385563422 l 0.0,0.0 c 0.0,-1.0569932801 0.856862354124,-1.91385563422 1.91385563422,-1.91385563422 Z"
+            android:valueType="pathType"
+            android:interpolator="@android:interpolator/linear" />
+        <objectAnimator
+            android:duration="37"
+            android:propertyName="pathData"
+            android:valueFrom="M -0.51686619465,-1.91385563422 l 1.0337323893,0.0 c 1.0569932801,0.0 1.91385563422,0.856862354124 1.91385563422,1.91385563422 l 0.0,0.0 c 0.0,1.0569932801 -0.856862354124,1.91385563422 -1.91385563422,1.91385563422 l -1.0337323893,0.0 c -1.0569932801,0.0 -1.91385563422,-0.856862354124 -1.91385563422,-1.91385563422 l 0.0,0.0 c 0.0,-1.0569932801 0.856862354124,-1.91385563422 1.91385563422,-1.91385563422 Z"
+            android:valueTo="M -0.209284646944,-1.96511922551 l 0.418569293889,0.0 c 1.08530537979,0.0 1.96511922551,0.879813845722 1.96511922551,1.96511922551 l 0.0,0.0 c 0.0,1.08530537979 -0.879813845722,1.96511922551 -1.96511922551,1.96511922551 l -0.418569293889,0.0 c -1.08530537979,0.0 -1.96511922551,-0.879813845722 -1.96511922551,-1.96511922551 l 0.0,0.0 c 0.0,-1.08530537979 0.879813845722,-1.96511922551 1.96511922551,-1.96511922551 Z"
+            android:valueType="pathType"
+            android:interpolator="@android:interpolator/linear" />
+        <objectAnimator
+            android:duration="37"
+            android:propertyName="pathData"
+            android:valueFrom="M -0.209284646944,-1.96511922551 l 0.418569293889,0.0 c 1.08530537979,0.0 1.96511922551,0.879813845722 1.96511922551,1.96511922551 l 0.0,0.0 c 0.0,1.08530537979 -0.879813845722,1.96511922551 -1.96511922551,1.96511922551 l -0.418569293889,0.0 c -1.08530537979,0.0 -1.96511922551,-0.879813845722 -1.96511922551,-1.96511922551 l 0.0,0.0 c 0.0,-1.08530537979 0.879813845722,-1.96511922551 1.96511922551,-1.96511922551 Z"
+            android:valueTo="M -0.0482966601849,-1.99195055664 l 0.0965933203697,0.0 c 1.10012391479,0.0 1.99195055664,0.89182664185 1.99195055664,1.99195055664 l 0.0,0.0 c 0.0,1.10012391479 -0.89182664185,1.99195055664 -1.99195055664,1.99195055664 l -0.0965933203697,0.0 c -1.10012391479,0.0 -1.99195055664,-0.89182664185 -1.99195055664,-1.99195055664 l 0.0,0.0 c 0.0,-1.10012391479 0.89182664185,-1.99195055664 1.99195055664,-1.99195055664 Z"
+            android:valueType="pathType"
+            android:interpolator="@android:interpolator/linear" />
+        <objectAnimator
+            android:duration="37"
+            android:propertyName="pathData"
+            android:valueFrom="M -0.0482966601849,-1.99195055664 l 0.0965933203697,0.0 c 1.10012391479,0.0 1.99195055664,0.89182664185 1.99195055664,1.99195055664 l 0.0,0.0 c 0.0,1.10012391479 -0.89182664185,1.99195055664 -1.99195055664,1.99195055664 l -0.0965933203697,0.0 c -1.10012391479,0.0 -1.99195055664,-0.89182664185 -1.99195055664,-1.99195055664 l 0.0,0.0 c 0.0,-1.10012391479 0.89182664185,-1.99195055664 1.99195055664,-1.99195055664 Z"
+            android:valueTo="M 0.0,-2.0 l 0.0,0.0 c 1.1045694996,0.0 2.0,0.8954305004 2.0,2.0 l 0.0,0.0 c 0.0,1.1045694996 -0.8954305004,2.0 -2.0,2.0 l 0.0,0.0 c -1.1045694996,0.0 -2.0,-0.8954305004 -2.0,-2.0 l 0.0,0.0 c 0.0,-1.1045694996 0.8954305004,-2.0 2.0,-2.0 Z"
+            android:valueType="pathType"
+            android:interpolator="@android:interpolator/linear" />
+    </set>
+</set>
diff --git a/core/res/res/anim/ft_avd_toarrow_rectangle_path_4_animation.xml b/core/res/res/anim/ft_avd_toarrow_rectangle_path_4_animation.xml
new file mode 100644
index 0000000..db294c7
--- /dev/null
+++ b/core/res/res/anim/ft_avd_toarrow_rectangle_path_4_animation.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You 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.
+*/
+-->
+<set
+    xmlns:android="http://schemas.android.com/apk/res/android" >
+    <set
+        android:ordering="sequentially" >
+        <objectAnimator
+            android:duration="100"
+            android:propertyName="pathData"
+            android:valueFrom="M 0.0,-2.0 l 0.0,0.0 c 1.1045694996,0.0 2.0,0.8954305004 2.0,2.0 l 0.0,0.0 c 0.0,1.1045694996 -0.8954305004,2.0 -2.0,2.0 l 0.0,0.0 c -1.1045694996,0.0 -2.0,-0.8954305004 -2.0,-2.0 l 0.0,0.0 c 0.0,-1.1045694996 0.8954305004,-2.0 2.0,-2.0 Z"
+            android:valueTo="M 0.0,-2.0 l 0.0,0.0 c 1.1045694996,0.0 2.0,0.8954305004 2.0,2.0 l 0.0,0.0 c 0.0,1.1045694996 -0.8954305004,2.0 -2.0,2.0 l 0.0,0.0 c -1.1045694996,0.0 -2.0,-0.8954305004 -2.0,-2.0 l 0.0,0.0 c 0.0,-1.1045694996 0.8954305004,-2.0 2.0,-2.0 Z"
+            android:valueType="pathType"
+            android:interpolator="@android:interpolator/linear" />
+        <objectAnimator
+            android:duration="49"
+            android:propertyName="pathData"
+            android:valueFrom="M 0.0,-2.0 l 0.0,0.0 c 1.1045694996,0.0 2.0,0.8954305004 2.0,2.0 l 0.0,0.0 c 0.0,1.1045694996 -0.8954305004,2.0 -2.0,2.0 l 0.0,0.0 c -1.1045694996,0.0 -2.0,-0.8954305004 -2.0,-2.0 l 0.0,0.0 c 0.0,-1.1045694996 0.8954305004,-2.0 2.0,-2.0 Z"
+            android:valueTo="M -4.0950630677,-1.30952224204 l 8.1901261354,0.0 c 0.177619793131,0.0 0.32160908516,0.143989292029 0.32160908516,0.32160908516 l 0.0,1.97582631376 c 0.0,0.177619793131 -0.143989292029,0.32160908516 -0.32160908516,0.32160908516 l -8.1901261354,0.0 c -0.177619793131,0.0 -0.32160908516,-0.143989292029 -0.32160908516,-0.32160908516 l 0.0,-1.97582631376 c 0.0,-0.177619793131 0.143989292029,-0.32160908516 0.32160908516,-0.32160908516 Z"
+            android:valueType="pathType"
+            android:interpolator="@android:interpolator/linear" />
+        <objectAnimator
+            android:duration="50"
+            android:propertyName="pathData"
+            android:valueFrom="M -4.0950630677,-1.30952224204 l 8.1901261354,0.0 c 0.177619793131,0.0 0.32160908516,0.143989292029 0.32160908516,0.32160908516 l 0.0,1.97582631376 c 0.0,0.177619793131 -0.143989292029,0.32160908516 -0.32160908516,0.32160908516 l -8.1901261354,0.0 c -0.177619793131,0.0 -0.32160908516,-0.143989292029 -0.32160908516,-0.32160908516 l 0.0,-1.97582631376 c 0.0,-0.177619793131 0.143989292029,-0.32160908516 0.32160908516,-0.32160908516 Z"
+            android:valueTo="M -5.11454284324,-1.11013061622 l 10.2290856865,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,2.22026123243 c 0.0,0.0 0.0,0.0 0.0,0.0 l -10.2290856865,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-2.22026123243 c 0.0,0.0 0.0,0.0 0.0,0.0 Z"
+            android:valueType="pathType"
+            android:interpolator="@android:interpolator/linear" />
+        <objectAnimator
+            android:duration="49"
+            android:propertyName="pathData"
+            android:valueFrom="M -5.11454284324,-1.11013061622 l 10.2290856865,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,2.22026123243 c 0.0,0.0 0.0,0.0 0.0,0.0 l -10.2290856865,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-2.22026123243 c 0.0,0.0 0.0,0.0 0.0,0.0 Z"
+            android:valueTo="M -5.41755510258,-1.02355568498 l 10.8351102052,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,2.04711136995 c 0.0,0.0 0.0,0.0 0.0,0.0 l -10.8351102052,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-2.04711136995 c 0.0,0.0 0.0,0.0 0.0,0.0 Z"
+            android:valueType="pathType"
+            android:interpolator="@android:interpolator/linear" />
+        <objectAnimator
+            android:duration="49"
+            android:propertyName="pathData"
+            android:valueFrom="M -5.41755510258,-1.02355568498 l 10.8351102052,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,2.04711136995 c 0.0,0.0 0.0,0.0 0.0,0.0 l -10.8351102052,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-2.04711136995 c 0.0,0.0 0.0,0.0 0.0,0.0 Z"
+            android:valueTo="M -5.5,-1.0 l 11.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l -11.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z"
+            android:valueType="pathType"
+            android:interpolator="@android:interpolator/linear" />
+    </set>
+</set>
diff --git a/core/res/res/anim/ft_avd_toarrow_rectangle_path_5_animation.xml b/core/res/res/anim/ft_avd_toarrow_rectangle_path_5_animation.xml
new file mode 100644
index 0000000..86e4dd6
--- /dev/null
+++ b/core/res/res/anim/ft_avd_toarrow_rectangle_path_5_animation.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You 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.
+*/
+-->
+<set
+    xmlns:android="http://schemas.android.com/apk/res/android" >
+    <set
+        android:ordering="sequentially" >
+        <objectAnimator
+            android:duration="50"
+            android:propertyName="pathData"
+            android:valueFrom="M 0.0,-2.0 l 0.0,0.0 c 1.1045694996,0.0 2.0,0.8954305004 2.0,2.0 l 0.0,0.0 c 0.0,1.1045694996 -0.8954305004,2.0 -2.0,2.0 l 0.0,0.0 c -1.1045694996,0.0 -2.0,-0.8954305004 -2.0,-2.0 l 0.0,0.0 c 0.0,-1.1045694996 0.8954305004,-2.0 2.0,-2.0 Z"
+            android:valueTo="M 0.0,-2.0 l 0.0,0.0 c 1.1045694996,0.0 2.0,0.8954305004 2.0,2.0 l 0.0,0.0 c 0.0,1.1045694996 -0.8954305004,2.0 -2.0,2.0 l 0.0,0.0 c -1.1045694996,0.0 -2.0,-0.8954305004 -2.0,-2.0 l 0.0,0.0 c 0.0,-1.1045694996 0.8954305004,-2.0 2.0,-2.0 Z"
+            android:valueType="pathType"
+            android:interpolator="@android:interpolator/linear" />
+        <objectAnimator
+            android:duration="41"
+            android:propertyName="pathData"
+            android:valueFrom="M 0.0,-2.0 l 0.0,0.0 c 1.1045694996,0.0 2.0,0.8954305004 2.0,2.0 l 0.0,0.0 c 0.0,1.1045694996 -0.8954305004,2.0 -2.0,2.0 l 0.0,0.0 c -1.1045694996,0.0 -2.0,-0.8954305004 -2.0,-2.0 l 0.0,0.0 c 0.0,-1.1045694996 0.8954305004,-2.0 2.0,-2.0 Z"
+            android:valueTo="M -0.510644950991,-1.91489250817 l 1.02128990198,0.0 c 1.05756592977,0.0 1.91489250817,0.857326578401 1.91489250817,1.91489250817 l 0.0,0.0 c 0.0,1.05756592977 -0.857326578401,1.91489250817 -1.91489250817,1.91489250817 l -1.02128990198,0.0 c -1.05756592977,0.0 -1.91489250817,-0.857326578401 -1.91489250817,-1.91489250817 l 0.0,0.0 c 0.0,-1.05756592977 0.857326578401,-1.91489250817 1.91489250817,-1.91489250817 Z"
+            android:valueType="pathType"
+            android:interpolator="@android:interpolator/linear" />
+        <objectAnimator
+            android:duration="41"
+            android:propertyName="pathData"
+            android:valueFrom="M -0.510644950991,-1.91489250817 l 1.02128990198,0.0 c 1.05756592977,0.0 1.91489250817,0.857326578401 1.91489250817,1.91489250817 l 0.0,0.0 c 0.0,1.05756592977 -0.857326578401,1.91489250817 -1.91489250817,1.91489250817 l -1.02128990198,0.0 c -1.05756592977,0.0 -1.91489250817,-0.857326578401 -1.91489250817,-1.91489250817 l 0.0,0.0 c 0.0,-1.05756592977 0.857326578401,-1.91489250817 1.91489250817,-1.91489250817 Z"
+            android:valueTo="M -3.66172292328,-1.54166666667 l 7.32344584656,0.0 c 0.347908322704,0.0 0.629943743386,0.282035420682 0.629943743386,0.629943743386 l 0.0,1.82344584656 c 0.0,0.347908322704 -0.282035420682,0.629943743386 -0.629943743386,0.629943743386 l -7.32344584656,0.0 c -0.347908322704,0.0 -0.629943743386,-0.282035420682 -0.629943743386,-0.629943743386 l 0.0,-1.82344584656 c 0.0,-0.347908322704 0.282035420682,-0.629943743386 0.629943743386,-0.629943743386 Z"
+            android:valueType="pathType"
+            android:interpolator="@android:interpolator/linear" />
+        <objectAnimator
+            android:duration="41"
+            android:propertyName="pathData"
+            android:valueFrom="M -3.66172292328,-1.54166666667 l 7.32344584656,0.0 c 0.347908322704,0.0 0.629943743386,0.282035420682 0.629943743386,0.629943743386 l 0.0,1.82344584656 c 0.0,0.347908322704 -0.282035420682,0.629943743386 -0.629943743386,0.629943743386 l -7.32344584656,0.0 c -0.347908322704,0.0 -0.629943743386,-0.282035420682 -0.629943743386,-0.629943743386 l 0.0,-1.82344584656 c 0.0,-0.347908322704 0.282035420682,-0.629943743386 0.629943743386,-0.629943743386 Z"
+            android:valueTo="M -5.80605656225,-1.22447422869 l 11.6121131245,0.0 c 0.0395282866537,0.0 0.0715722943065,0.0320440076528 0.0715722943065,0.0715722943065 l 0.0,2.30580386877 c 0.0,0.0395282866537 -0.0320440076528,0.0715722943065 -0.0715722943065,0.0715722943065 l -11.6121131245,0.0 c -0.0395282866537,0.0 -0.0715722943065,-0.0320440076528 -0.0715722943065,-0.0715722943065 l 0.0,-2.30580386877 c 0.0,-0.0395282866537 0.0320440076528,-0.0715722943065 0.0715722943065,-0.0715722943065 Z"
+            android:valueType="pathType"
+            android:interpolator="@android:interpolator/linear" />
+        <objectAnimator
+            android:duration="41"
+            android:propertyName="pathData"
+            android:valueFrom="M -5.80605656225,-1.22447422869 l 11.6121131245,0.0 c 0.0395282866537,0.0 0.0715722943065,0.0320440076528 0.0715722943065,0.0715722943065 l 0.0,2.30580386877 c 0.0,0.0395282866537 -0.0320440076528,0.0715722943065 -0.0715722943065,0.0715722943065 l -11.6121131245,0.0 c -0.0395282866537,0.0 -0.0715722943065,-0.0320440076528 -0.0715722943065,-0.0715722943065 l 0.0,-2.30580386877 c 0.0,-0.0395282866537 0.0320440076528,-0.0715722943065 0.0715722943065,-0.0715722943065 Z"
+            android:valueTo="M -6.60386380145,-1.07922723971 l 13.2077276029,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,2.15845447942 c 0.0,0.0 0.0,0.0 0.0,0.0 l -13.2077276029,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-2.15845447942 c 0.0,0.0 0.0,0.0 0.0,0.0 Z"
+            android:valueType="pathType"
+            android:interpolator="@android:interpolator/linear" />
+        <objectAnimator
+            android:duration="41"
+            android:propertyName="pathData"
+            android:valueFrom="M -6.60386380145,-1.07922723971 l 13.2077276029,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,2.15845447942 c 0.0,0.0 0.0,0.0 0.0,0.0 l -13.2077276029,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-2.15845447942 c 0.0,0.0 0.0,0.0 0.0,0.0 Z"
+            android:valueTo="M -6.91679014084,-1.01664197183 l 13.8335802817,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,2.03328394367 c 0.0,0.0 0.0,0.0 0.0,0.0 l -13.8335802817,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-2.03328394367 c 0.0,0.0 0.0,0.0 0.0,0.0 Z"
+            android:valueType="pathType"
+            android:interpolator="@android:interpolator/linear" />
+        <objectAnimator
+            android:duration="41"
+            android:propertyName="pathData"
+            android:valueFrom="M -6.91679014084,-1.01664197183 l 13.8335802817,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,2.03328394367 c 0.0,0.0 0.0,0.0 0.0,0.0 l -13.8335802817,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-2.03328394367 c 0.0,0.0 0.0,0.0 0.0,0.0 Z"
+            android:valueTo="M -7.0,-1.0 l 14.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l -14.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z"
+            android:valueType="pathType"
+            android:interpolator="@android:interpolator/linear" />
+    </set>
+</set>
diff --git a/core/res/res/anim/ft_avd_toarrow_rectangle_path_6_animation.xml b/core/res/res/anim/ft_avd_toarrow_rectangle_path_6_animation.xml
new file mode 100644
index 0000000..db294c7
--- /dev/null
+++ b/core/res/res/anim/ft_avd_toarrow_rectangle_path_6_animation.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You 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.
+*/
+-->
+<set
+    xmlns:android="http://schemas.android.com/apk/res/android" >
+    <set
+        android:ordering="sequentially" >
+        <objectAnimator
+            android:duration="100"
+            android:propertyName="pathData"
+            android:valueFrom="M 0.0,-2.0 l 0.0,0.0 c 1.1045694996,0.0 2.0,0.8954305004 2.0,2.0 l 0.0,0.0 c 0.0,1.1045694996 -0.8954305004,2.0 -2.0,2.0 l 0.0,0.0 c -1.1045694996,0.0 -2.0,-0.8954305004 -2.0,-2.0 l 0.0,0.0 c 0.0,-1.1045694996 0.8954305004,-2.0 2.0,-2.0 Z"
+            android:valueTo="M 0.0,-2.0 l 0.0,0.0 c 1.1045694996,0.0 2.0,0.8954305004 2.0,2.0 l 0.0,0.0 c 0.0,1.1045694996 -0.8954305004,2.0 -2.0,2.0 l 0.0,0.0 c -1.1045694996,0.0 -2.0,-0.8954305004 -2.0,-2.0 l 0.0,0.0 c 0.0,-1.1045694996 0.8954305004,-2.0 2.0,-2.0 Z"
+            android:valueType="pathType"
+            android:interpolator="@android:interpolator/linear" />
+        <objectAnimator
+            android:duration="49"
+            android:propertyName="pathData"
+            android:valueFrom="M 0.0,-2.0 l 0.0,0.0 c 1.1045694996,0.0 2.0,0.8954305004 2.0,2.0 l 0.0,0.0 c 0.0,1.1045694996 -0.8954305004,2.0 -2.0,2.0 l 0.0,0.0 c -1.1045694996,0.0 -2.0,-0.8954305004 -2.0,-2.0 l 0.0,0.0 c 0.0,-1.1045694996 0.8954305004,-2.0 2.0,-2.0 Z"
+            android:valueTo="M -4.0950630677,-1.30952224204 l 8.1901261354,0.0 c 0.177619793131,0.0 0.32160908516,0.143989292029 0.32160908516,0.32160908516 l 0.0,1.97582631376 c 0.0,0.177619793131 -0.143989292029,0.32160908516 -0.32160908516,0.32160908516 l -8.1901261354,0.0 c -0.177619793131,0.0 -0.32160908516,-0.143989292029 -0.32160908516,-0.32160908516 l 0.0,-1.97582631376 c 0.0,-0.177619793131 0.143989292029,-0.32160908516 0.32160908516,-0.32160908516 Z"
+            android:valueType="pathType"
+            android:interpolator="@android:interpolator/linear" />
+        <objectAnimator
+            android:duration="50"
+            android:propertyName="pathData"
+            android:valueFrom="M -4.0950630677,-1.30952224204 l 8.1901261354,0.0 c 0.177619793131,0.0 0.32160908516,0.143989292029 0.32160908516,0.32160908516 l 0.0,1.97582631376 c 0.0,0.177619793131 -0.143989292029,0.32160908516 -0.32160908516,0.32160908516 l -8.1901261354,0.0 c -0.177619793131,0.0 -0.32160908516,-0.143989292029 -0.32160908516,-0.32160908516 l 0.0,-1.97582631376 c 0.0,-0.177619793131 0.143989292029,-0.32160908516 0.32160908516,-0.32160908516 Z"
+            android:valueTo="M -5.11454284324,-1.11013061622 l 10.2290856865,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,2.22026123243 c 0.0,0.0 0.0,0.0 0.0,0.0 l -10.2290856865,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-2.22026123243 c 0.0,0.0 0.0,0.0 0.0,0.0 Z"
+            android:valueType="pathType"
+            android:interpolator="@android:interpolator/linear" />
+        <objectAnimator
+            android:duration="49"
+            android:propertyName="pathData"
+            android:valueFrom="M -5.11454284324,-1.11013061622 l 10.2290856865,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,2.22026123243 c 0.0,0.0 0.0,0.0 0.0,0.0 l -10.2290856865,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-2.22026123243 c 0.0,0.0 0.0,0.0 0.0,0.0 Z"
+            android:valueTo="M -5.41755510258,-1.02355568498 l 10.8351102052,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,2.04711136995 c 0.0,0.0 0.0,0.0 0.0,0.0 l -10.8351102052,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-2.04711136995 c 0.0,0.0 0.0,0.0 0.0,0.0 Z"
+            android:valueType="pathType"
+            android:interpolator="@android:interpolator/linear" />
+        <objectAnimator
+            android:duration="49"
+            android:propertyName="pathData"
+            android:valueFrom="M -5.41755510258,-1.02355568498 l 10.8351102052,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,2.04711136995 c 0.0,0.0 0.0,0.0 0.0,0.0 l -10.8351102052,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-2.04711136995 c 0.0,0.0 0.0,0.0 0.0,0.0 Z"
+            android:valueTo="M -5.5,-1.0 l 11.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l -11.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z"
+            android:valueType="pathType"
+            android:interpolator="@android:interpolator/linear" />
+    </set>
+</set>
diff --git a/core/res/res/anim/ft_avd_tooverflow_rectangle_1_animation.xml b/core/res/res/anim/ft_avd_tooverflow_rectangle_1_animation.xml
new file mode 100644
index 0000000..3869ced
--- /dev/null
+++ b/core/res/res/anim/ft_avd_tooverflow_rectangle_1_animation.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You 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.
+*/
+-->
+<set
+    xmlns:android="http://schemas.android.com/apk/res/android" >
+    <objectAnimator
+        android:duration="400"
+        android:propertyXName="translateX"
+        android:propertyYName="translateY"
+        android:pathData="M -6.5,0.0 c 1.08333,-1.0 5.41667,-5.0 6.5,-6.0"
+        android:interpolator="@android:interpolator/fast_out_slow_in" />
+    <objectAnimator
+        android:duration="400"
+        android:propertyName="rotation"
+        android:valueFrom="-45.0"
+        android:valueTo="0.0"
+        android:valueType="floatType"
+        android:interpolator="@android:interpolator/fast_out_slow_in" />
+</set>
diff --git a/core/res/res/anim/ft_avd_tooverflow_rectangle_1_pivot_animation.xml b/core/res/res/anim/ft_avd_tooverflow_rectangle_1_pivot_animation.xml
new file mode 100644
index 0000000..90010a75
--- /dev/null
+++ b/core/res/res/anim/ft_avd_tooverflow_rectangle_1_pivot_animation.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You 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.
+*/
+-->
+<set
+    xmlns:android="http://schemas.android.com/apk/res/android" >
+    <objectAnimator
+        android:duration="300"
+        android:propertyXName="translateX"
+        android:propertyYName="translateY"
+        android:pathData="M 4.5,0.0 c -0.75,0.0 -3.75,0.0 -4.5,0.0"
+        android:interpolator="@android:interpolator/fast_out_slow_in" />
+</set>
diff --git a/core/res/res/anim/ft_avd_tooverflow_rectangle_2_animation.xml b/core/res/res/anim/ft_avd_tooverflow_rectangle_2_animation.xml
new file mode 100644
index 0000000..b5e031e
--- /dev/null
+++ b/core/res/res/anim/ft_avd_tooverflow_rectangle_2_animation.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You 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.
+*/
+-->
+<set
+    xmlns:android="http://schemas.android.com/apk/res/android" >
+    <objectAnimator
+        android:duration="300"
+        android:propertyXName="translateX"
+        android:propertyYName="translateY"
+        android:pathData="M -8.0,0.0 c 1.33333,0.0 6.66667,0.0 8.0,0.0"
+        android:interpolator="@android:interpolator/fast_out_slow_in" />
+</set>
diff --git a/core/res/res/anim/ft_avd_tooverflow_rectangle_2_pivot_animation.xml b/core/res/res/anim/ft_avd_tooverflow_rectangle_2_pivot_animation.xml
new file mode 100644
index 0000000..55c72c6e
--- /dev/null
+++ b/core/res/res/anim/ft_avd_tooverflow_rectangle_2_pivot_animation.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You 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.
+*/
+-->
+<set
+    xmlns:android="http://schemas.android.com/apk/res/android" >
+    <objectAnimator
+        android:duration="216"
+        android:propertyXName="translateX"
+        android:propertyYName="translateY"
+        android:pathData="M 9.0,0.0 c -1.5,0.0 -7.5,0.0 -9.0,0.0"
+        android:interpolator="@android:interpolator/fast_out_slow_in" />
+</set>
diff --git a/core/res/res/anim/ft_avd_tooverflow_rectangle_3_animation.xml b/core/res/res/anim/ft_avd_tooverflow_rectangle_3_animation.xml
new file mode 100644
index 0000000..0524f2a
--- /dev/null
+++ b/core/res/res/anim/ft_avd_tooverflow_rectangle_3_animation.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You 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.
+*/
+-->
+<set
+    xmlns:android="http://schemas.android.com/apk/res/android" >
+    <objectAnimator
+        android:duration="400"
+        android:propertyXName="translateX"
+        android:propertyYName="translateY"
+        android:pathData="M -6.5,0.0 c 1.08333,1.0 5.41667,5.0 6.5,6.0"
+        android:interpolator="@android:interpolator/fast_out_slow_in" />
+    <objectAnimator
+        android:duration="400"
+        android:propertyName="rotation"
+        android:valueFrom="45.0"
+        android:valueTo="0.0"
+        android:valueType="floatType"
+        android:interpolator="@android:interpolator/fast_out_slow_in" />
+</set>
diff --git a/core/res/res/anim/ft_avd_tooverflow_rectangle_3_pivot_animation.xml b/core/res/res/anim/ft_avd_tooverflow_rectangle_3_pivot_animation.xml
new file mode 100644
index 0000000..90010a75
--- /dev/null
+++ b/core/res/res/anim/ft_avd_tooverflow_rectangle_3_pivot_animation.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You 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.
+*/
+-->
+<set
+    xmlns:android="http://schemas.android.com/apk/res/android" >
+    <objectAnimator
+        android:duration="300"
+        android:propertyXName="translateX"
+        android:propertyYName="translateY"
+        android:pathData="M 4.5,0.0 c -0.75,0.0 -3.75,0.0 -4.5,0.0"
+        android:interpolator="@android:interpolator/fast_out_slow_in" />
+</set>
diff --git a/core/res/res/anim/ft_avd_tooverflow_rectangle_path_1_animation.xml b/core/res/res/anim/ft_avd_tooverflow_rectangle_path_1_animation.xml
new file mode 100644
index 0000000..ced8cf5
--- /dev/null
+++ b/core/res/res/anim/ft_avd_tooverflow_rectangle_path_1_animation.xml
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You 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.
+*/
+-->
+<set
+    xmlns:android="http://schemas.android.com/apk/res/android" >
+    <set
+        android:ordering="sequentially" >
+        <objectAnimator
+            android:duration="37"
+            android:propertyName="pathData"
+            android:valueFrom="M -5.5,-1.0 l 11.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l -11.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z"
+            android:valueTo="M -5.349609375,-1.04296875 l 10.69921875,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,2.0859375 c 0.0,0.0 0.0,0.0 0.0,0.0 l -10.69921875,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-2.0859375 c 0.0,0.0 0.0,0.0 0.0,0.0 Z"
+            android:valueType="pathType"
+            android:interpolator="@android:interpolator/linear" />
+        <objectAnimator
+            android:duration="37"
+            android:propertyName="pathData"
+            android:valueFrom="M -5.349609375,-1.04296875 l 10.69921875,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,2.0859375 c 0.0,0.0 0.0,0.0 0.0,0.0 l -10.69921875,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-2.0859375 c 0.0,0.0 0.0,0.0 0.0,0.0 Z"
+            android:valueTo="M -4.57327108594,-1.25 l 9.14654217189,0.0 c 0.0285690903566,0.0 0.0517289140556,0.023159823699 0.0517289140556,0.0517289140556 l 0.0,2.39654217189 c 0.0,0.0285690903566 -0.023159823699,0.0517289140556 -0.0517289140556,0.0517289140556 l -9.14654217189,0.0 c -0.0285690903566,0.0 -0.0517289140556,-0.023159823699 -0.0517289140556,-0.0517289140556 l 0.0,-2.39654217189 c 0.0,-0.0285690903566 0.023159823699,-0.0517289140556 0.0517289140556,-0.0517289140556 Z"
+            android:valueType="pathType"
+            android:interpolator="@android:interpolator/linear" />
+        <objectAnimator
+            android:duration="37"
+            android:propertyName="pathData"
+            android:valueFrom="M -4.57327108594,-1.25 l 9.14654217189,0.0 c 0.0285690903566,0.0 0.0517289140556,0.023159823699 0.0517289140556,0.0517289140556 l 0.0,2.39654217189 c 0.0,0.0285690903566 -0.023159823699,0.0517289140556 -0.0517289140556,0.0517289140556 l -9.14654217189,0.0 c -0.0285690903566,0.0 -0.0517289140556,-0.023159823699 -0.0517289140556,-0.0517289140556 l 0.0,-2.39654217189 c 0.0,-0.0285690903566 0.023159823699,-0.0517289140556 0.0517289140556,-0.0517289140556 Z"
+            android:valueTo="M -3.04137381938,-1.55960748018 l 6.08274763876,0.0 c 0.2761423749,0.0 0.5,0.2238576251 0.5,0.5 l 0.0,2.11921496035 c 0.0,0.2761423749 -0.2238576251,0.5 -0.5,0.5 l -6.08274763876,0.0 c -0.2761423749,0.0 -0.5,-0.2238576251 -0.5,-0.5 l 0.0,-2.11921496035 c 0.0,-0.2761423749 0.2238576251,-0.5 0.5,-0.5 Z"
+            android:valueType="pathType"
+            android:interpolator="@android:interpolator/linear" />
+        <objectAnimator
+            android:duration="37"
+            android:propertyName="pathData"
+            android:valueFrom="M -3.04137381938,-1.55960748018 l 6.08274763876,0.0 c 0.2761423749,0.0 0.5,0.2238576251 0.5,0.5 l 0.0,2.11921496035 c 0.0,0.2761423749 -0.2238576251,0.5 -0.5,0.5 l -6.08274763876,0.0 c -0.2761423749,0.0 -0.5,-0.2238576251 -0.5,-0.5 l 0.0,-2.11921496035 c 0.0,-0.2761423749 0.2238576251,-0.5 0.5,-0.5 Z"
+            android:valueTo="M -1.55858989728,-1.77552577131 l 3.11717979456,0.0 c 0.677691994437,0.0 1.22706990313,0.549377908693 1.22706990313,1.22706990313 l 0.0,1.09691173636 c 0.0,0.677691994437 -0.549377908693,1.22706990313 -1.22706990313,1.22706990313 l -3.11717979456,0.0 c -0.677691994437,0.0 -1.22706990313,-0.549377908693 -1.22706990313,-1.22706990313 l 0.0,-1.09691173636 c 0.0,-0.677691994437 0.549377908693,-1.22706990313 1.22706990313,-1.22706990313 Z"
+            android:valueType="pathType"
+            android:interpolator="@android:interpolator/linear" />
+        <objectAnimator
+            android:duration="37"
+            android:propertyName="pathData"
+            android:valueFrom="M -1.55858989728,-1.77552577131 l 3.11717979456,0.0 c 0.677691994437,0.0 1.22706990313,0.549377908693 1.22706990313,1.22706990313 l 0.0,1.09691173636 c 0.0,0.677691994437 -0.549377908693,1.22706990313 -1.22706990313,1.22706990313 l -3.11717979456,0.0 c -0.677691994437,0.0 -1.22706990313,-0.549377908693 -1.22706990313,-1.22706990313 l 0.0,-1.09691173636 c 0.0,-0.677691994437 0.549377908693,-1.22706990313 1.22706990313,-1.22706990313 Z"
+            android:valueTo="M -0.706008791281,-1.89447268498 l 1.41201758256,0.0 c 0.918635554655,0.0 1.66333681129,0.744701256633 1.66333681129,1.66333681129 l 0.0,0.462271747384 c 0.0,0.918635554655 -0.744701256633,1.66333681129 -1.66333681129,1.66333681129 l -1.41201758256,0.0 c -0.918635554655,0.0 -1.66333681129,-0.744701256633 -1.66333681129,-1.66333681129 l 0.0,-0.462271747384 c 0.0,-0.918635554655 0.744701256633,-1.66333681129 1.66333681129,-1.66333681129 Z"
+            android:valueType="pathType"
+            android:interpolator="@android:interpolator/linear" />
+        <objectAnimator
+            android:duration="37"
+            android:propertyName="pathData"
+            android:valueFrom="M -0.706008791281,-1.89447268498 l 1.41201758256,0.0 c 0.918635554655,0.0 1.66333681129,0.744701256633 1.66333681129,1.66333681129 l 0.0,0.462271747384 c 0.0,0.918635554655 -0.744701256633,1.66333681129 -1.66333681129,1.66333681129 l -1.41201758256,0.0 c -0.918635554655,0.0 -1.66333681129,-0.744701256633 -1.66333681129,-1.66333681129 l 0.0,-0.462271747384 c 0.0,-0.918635554655 0.744701256633,-1.66333681129 1.66333681129,-1.66333681129 Z"
+            android:valueTo="M -0.265730251554,-1.95936709392 l 0.531460503108,0.0 c 1.03635400439,0.0 1.87648491973,0.840130915331 1.87648491973,1.87648491973 l 0.0,0.165764348389 c 0.0,1.03635400439 -0.840130915331,1.87648491973 -1.87648491973,1.87648491973 l -0.531460503108,0.0 c -1.03635400439,0.0 -1.87648491973,-0.840130915331 -1.87648491973,-1.87648491973 l 0.0,-0.165764348389 c 0.0,-1.03635400439 0.840130915331,-1.87648491973 1.87648491973,-1.87648491973 Z"
+            android:valueType="pathType"
+            android:interpolator="@android:interpolator/linear" />
+        <objectAnimator
+            android:duration="37"
+            android:propertyName="pathData"
+            android:valueFrom="M -0.265730251554,-1.95936709392 l 0.531460503108,0.0 c 1.03635400439,0.0 1.87648491973,0.840130915331 1.87648491973,1.87648491973 l 0.0,0.165764348389 c 0.0,1.03635400439 -0.840130915331,1.87648491973 -1.87648491973,1.87648491973 l -0.531460503108,0.0 c -1.03635400439,0.0 -1.87648491973,-0.840130915331 -1.87648491973,-1.87648491973 l 0.0,-0.165764348389 c 0.0,-1.03635400439 0.840130915331,-1.87648491973 1.87648491973,-1.87648491973 Z"
+            android:valueTo="M -0.0581061000545,-1.99098433926 l 0.116212200109,0.0 c 1.08990562844,0.0 1.97344871252,0.883543084083 1.97344871252,1.97344871252 l 0.0,0.0350712534878 c 0.0,1.08990562844 -0.883543084083,1.97344871252 -1.97344871252,1.97344871252 l -0.116212200109,0.0 c -1.08990562844,0.0 -1.97344871252,-0.883543084083 -1.97344871252,-1.97344871252 l 0.0,-0.0350712534878 c 0.0,-1.08990562844 0.883543084083,-1.97344871252 1.97344871252,-1.97344871252 Z"
+            android:valueType="pathType"
+            android:interpolator="@android:interpolator/linear" />
+        <objectAnimator
+            android:duration="37"
+            android:propertyName="pathData"
+            android:valueFrom="M -0.0581061000545,-1.99098433926 l 0.116212200109,0.0 c 1.08990562844,0.0 1.97344871252,0.883543084083 1.97344871252,1.97344871252 l 0.0,0.0350712534878 c 0.0,1.08990562844 -0.883543084083,1.97344871252 -1.97344871252,1.97344871252 l -0.116212200109,0.0 c -1.08990562844,0.0 -1.97344871252,-0.883543084083 -1.97344871252,-1.97344871252 l 0.0,-0.0350712534878 c 0.0,-1.08990562844 0.883543084083,-1.97344871252 1.97344871252,-1.97344871252 Z"
+            android:valueTo="M 0.0,-2.0 l 0.0,0.0 c 1.1045694996,0.0 2.0,0.8954305004 2.0,2.0 l 0.0,0.0 c 0.0,1.1045694996 -0.8954305004,2.0 -2.0,2.0 l 0.0,0.0 c -1.1045694996,0.0 -2.0,-0.8954305004 -2.0,-2.0 l 0.0,0.0 c 0.0,-1.1045694996 0.8954305004,-2.0 2.0,-2.0 Z"
+            android:valueType="pathType"
+            android:interpolator="@android:interpolator/linear" />
+    </set>
+</set>
diff --git a/core/res/res/anim/ft_avd_tooverflow_rectangle_path_2_animation.xml b/core/res/res/anim/ft_avd_tooverflow_rectangle_path_2_animation.xml
new file mode 100644
index 0000000..ced8cf5
--- /dev/null
+++ b/core/res/res/anim/ft_avd_tooverflow_rectangle_path_2_animation.xml
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You 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.
+*/
+-->
+<set
+    xmlns:android="http://schemas.android.com/apk/res/android" >
+    <set
+        android:ordering="sequentially" >
+        <objectAnimator
+            android:duration="37"
+            android:propertyName="pathData"
+            android:valueFrom="M -5.5,-1.0 l 11.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l -11.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z"
+            android:valueTo="M -5.349609375,-1.04296875 l 10.69921875,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,2.0859375 c 0.0,0.0 0.0,0.0 0.0,0.0 l -10.69921875,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-2.0859375 c 0.0,0.0 0.0,0.0 0.0,0.0 Z"
+            android:valueType="pathType"
+            android:interpolator="@android:interpolator/linear" />
+        <objectAnimator
+            android:duration="37"
+            android:propertyName="pathData"
+            android:valueFrom="M -5.349609375,-1.04296875 l 10.69921875,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,2.0859375 c 0.0,0.0 0.0,0.0 0.0,0.0 l -10.69921875,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-2.0859375 c 0.0,0.0 0.0,0.0 0.0,0.0 Z"
+            android:valueTo="M -4.57327108594,-1.25 l 9.14654217189,0.0 c 0.0285690903566,0.0 0.0517289140556,0.023159823699 0.0517289140556,0.0517289140556 l 0.0,2.39654217189 c 0.0,0.0285690903566 -0.023159823699,0.0517289140556 -0.0517289140556,0.0517289140556 l -9.14654217189,0.0 c -0.0285690903566,0.0 -0.0517289140556,-0.023159823699 -0.0517289140556,-0.0517289140556 l 0.0,-2.39654217189 c 0.0,-0.0285690903566 0.023159823699,-0.0517289140556 0.0517289140556,-0.0517289140556 Z"
+            android:valueType="pathType"
+            android:interpolator="@android:interpolator/linear" />
+        <objectAnimator
+            android:duration="37"
+            android:propertyName="pathData"
+            android:valueFrom="M -4.57327108594,-1.25 l 9.14654217189,0.0 c 0.0285690903566,0.0 0.0517289140556,0.023159823699 0.0517289140556,0.0517289140556 l 0.0,2.39654217189 c 0.0,0.0285690903566 -0.023159823699,0.0517289140556 -0.0517289140556,0.0517289140556 l -9.14654217189,0.0 c -0.0285690903566,0.0 -0.0517289140556,-0.023159823699 -0.0517289140556,-0.0517289140556 l 0.0,-2.39654217189 c 0.0,-0.0285690903566 0.023159823699,-0.0517289140556 0.0517289140556,-0.0517289140556 Z"
+            android:valueTo="M -3.04137381938,-1.55960748018 l 6.08274763876,0.0 c 0.2761423749,0.0 0.5,0.2238576251 0.5,0.5 l 0.0,2.11921496035 c 0.0,0.2761423749 -0.2238576251,0.5 -0.5,0.5 l -6.08274763876,0.0 c -0.2761423749,0.0 -0.5,-0.2238576251 -0.5,-0.5 l 0.0,-2.11921496035 c 0.0,-0.2761423749 0.2238576251,-0.5 0.5,-0.5 Z"
+            android:valueType="pathType"
+            android:interpolator="@android:interpolator/linear" />
+        <objectAnimator
+            android:duration="37"
+            android:propertyName="pathData"
+            android:valueFrom="M -3.04137381938,-1.55960748018 l 6.08274763876,0.0 c 0.2761423749,0.0 0.5,0.2238576251 0.5,0.5 l 0.0,2.11921496035 c 0.0,0.2761423749 -0.2238576251,0.5 -0.5,0.5 l -6.08274763876,0.0 c -0.2761423749,0.0 -0.5,-0.2238576251 -0.5,-0.5 l 0.0,-2.11921496035 c 0.0,-0.2761423749 0.2238576251,-0.5 0.5,-0.5 Z"
+            android:valueTo="M -1.55858989728,-1.77552577131 l 3.11717979456,0.0 c 0.677691994437,0.0 1.22706990313,0.549377908693 1.22706990313,1.22706990313 l 0.0,1.09691173636 c 0.0,0.677691994437 -0.549377908693,1.22706990313 -1.22706990313,1.22706990313 l -3.11717979456,0.0 c -0.677691994437,0.0 -1.22706990313,-0.549377908693 -1.22706990313,-1.22706990313 l 0.0,-1.09691173636 c 0.0,-0.677691994437 0.549377908693,-1.22706990313 1.22706990313,-1.22706990313 Z"
+            android:valueType="pathType"
+            android:interpolator="@android:interpolator/linear" />
+        <objectAnimator
+            android:duration="37"
+            android:propertyName="pathData"
+            android:valueFrom="M -1.55858989728,-1.77552577131 l 3.11717979456,0.0 c 0.677691994437,0.0 1.22706990313,0.549377908693 1.22706990313,1.22706990313 l 0.0,1.09691173636 c 0.0,0.677691994437 -0.549377908693,1.22706990313 -1.22706990313,1.22706990313 l -3.11717979456,0.0 c -0.677691994437,0.0 -1.22706990313,-0.549377908693 -1.22706990313,-1.22706990313 l 0.0,-1.09691173636 c 0.0,-0.677691994437 0.549377908693,-1.22706990313 1.22706990313,-1.22706990313 Z"
+            android:valueTo="M -0.706008791281,-1.89447268498 l 1.41201758256,0.0 c 0.918635554655,0.0 1.66333681129,0.744701256633 1.66333681129,1.66333681129 l 0.0,0.462271747384 c 0.0,0.918635554655 -0.744701256633,1.66333681129 -1.66333681129,1.66333681129 l -1.41201758256,0.0 c -0.918635554655,0.0 -1.66333681129,-0.744701256633 -1.66333681129,-1.66333681129 l 0.0,-0.462271747384 c 0.0,-0.918635554655 0.744701256633,-1.66333681129 1.66333681129,-1.66333681129 Z"
+            android:valueType="pathType"
+            android:interpolator="@android:interpolator/linear" />
+        <objectAnimator
+            android:duration="37"
+            android:propertyName="pathData"
+            android:valueFrom="M -0.706008791281,-1.89447268498 l 1.41201758256,0.0 c 0.918635554655,0.0 1.66333681129,0.744701256633 1.66333681129,1.66333681129 l 0.0,0.462271747384 c 0.0,0.918635554655 -0.744701256633,1.66333681129 -1.66333681129,1.66333681129 l -1.41201758256,0.0 c -0.918635554655,0.0 -1.66333681129,-0.744701256633 -1.66333681129,-1.66333681129 l 0.0,-0.462271747384 c 0.0,-0.918635554655 0.744701256633,-1.66333681129 1.66333681129,-1.66333681129 Z"
+            android:valueTo="M -0.265730251554,-1.95936709392 l 0.531460503108,0.0 c 1.03635400439,0.0 1.87648491973,0.840130915331 1.87648491973,1.87648491973 l 0.0,0.165764348389 c 0.0,1.03635400439 -0.840130915331,1.87648491973 -1.87648491973,1.87648491973 l -0.531460503108,0.0 c -1.03635400439,0.0 -1.87648491973,-0.840130915331 -1.87648491973,-1.87648491973 l 0.0,-0.165764348389 c 0.0,-1.03635400439 0.840130915331,-1.87648491973 1.87648491973,-1.87648491973 Z"
+            android:valueType="pathType"
+            android:interpolator="@android:interpolator/linear" />
+        <objectAnimator
+            android:duration="37"
+            android:propertyName="pathData"
+            android:valueFrom="M -0.265730251554,-1.95936709392 l 0.531460503108,0.0 c 1.03635400439,0.0 1.87648491973,0.840130915331 1.87648491973,1.87648491973 l 0.0,0.165764348389 c 0.0,1.03635400439 -0.840130915331,1.87648491973 -1.87648491973,1.87648491973 l -0.531460503108,0.0 c -1.03635400439,0.0 -1.87648491973,-0.840130915331 -1.87648491973,-1.87648491973 l 0.0,-0.165764348389 c 0.0,-1.03635400439 0.840130915331,-1.87648491973 1.87648491973,-1.87648491973 Z"
+            android:valueTo="M -0.0581061000545,-1.99098433926 l 0.116212200109,0.0 c 1.08990562844,0.0 1.97344871252,0.883543084083 1.97344871252,1.97344871252 l 0.0,0.0350712534878 c 0.0,1.08990562844 -0.883543084083,1.97344871252 -1.97344871252,1.97344871252 l -0.116212200109,0.0 c -1.08990562844,0.0 -1.97344871252,-0.883543084083 -1.97344871252,-1.97344871252 l 0.0,-0.0350712534878 c 0.0,-1.08990562844 0.883543084083,-1.97344871252 1.97344871252,-1.97344871252 Z"
+            android:valueType="pathType"
+            android:interpolator="@android:interpolator/linear" />
+        <objectAnimator
+            android:duration="37"
+            android:propertyName="pathData"
+            android:valueFrom="M -0.0581061000545,-1.99098433926 l 0.116212200109,0.0 c 1.08990562844,0.0 1.97344871252,0.883543084083 1.97344871252,1.97344871252 l 0.0,0.0350712534878 c 0.0,1.08990562844 -0.883543084083,1.97344871252 -1.97344871252,1.97344871252 l -0.116212200109,0.0 c -1.08990562844,0.0 -1.97344871252,-0.883543084083 -1.97344871252,-1.97344871252 l 0.0,-0.0350712534878 c 0.0,-1.08990562844 0.883543084083,-1.97344871252 1.97344871252,-1.97344871252 Z"
+            android:valueTo="M 0.0,-2.0 l 0.0,0.0 c 1.1045694996,0.0 2.0,0.8954305004 2.0,2.0 l 0.0,0.0 c 0.0,1.1045694996 -0.8954305004,2.0 -2.0,2.0 l 0.0,0.0 c -1.1045694996,0.0 -2.0,-0.8954305004 -2.0,-2.0 l 0.0,0.0 c 0.0,-1.1045694996 0.8954305004,-2.0 2.0,-2.0 Z"
+            android:valueType="pathType"
+            android:interpolator="@android:interpolator/linear" />
+    </set>
+</set>
diff --git a/core/res/res/anim/ft_avd_tooverflow_rectangle_path_3_animation.xml b/core/res/res/anim/ft_avd_tooverflow_rectangle_path_3_animation.xml
new file mode 100644
index 0000000..cb29410
--- /dev/null
+++ b/core/res/res/anim/ft_avd_tooverflow_rectangle_path_3_animation.xml
@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You 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.
+*/
+-->
+<set
+    xmlns:android="http://schemas.android.com/apk/res/android" >
+    <set
+        android:ordering="sequentially" >
+        <objectAnimator
+            android:duration="50"
+            android:propertyName="pathData"
+            android:valueFrom="M -7.0,-1.0 l 14.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l -14.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z"
+            android:valueTo="M -7.0,-1.0 l 14.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l -14.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z"
+            android:valueType="pathType"
+            android:interpolator="@android:interpolator/linear" />
+        <objectAnimator
+            android:duration="37"
+            android:propertyName="pathData"
+            android:valueFrom="M -7.0,-1.0 l 14.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l -14.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z"
+            android:valueTo="M -4.36843359242,-1.49992262412 l 8.73686718484,0.0 c 0.0728757880921,0.0 0.131953286993,0.0590774989007 0.131953286993,0.131953286993 l 0.0,2.73593867425 c 0.0,0.0728757880921 -0.0590774989007,0.131953286993 -0.131953286993,0.131953286993 l -8.73686718484,0.0 c -0.0728757880921,0.0 -0.131953286993,-0.0590774989007 -0.131953286993,-0.131953286993 l 0.0,-2.73593867425 c 0.0,-0.0728757880921 0.0590774989007,-0.131953286993 0.131953286993,-0.131953286993 Z"
+            android:valueType="pathType"
+            android:interpolator="@android:interpolator/linear" />
+        <objectAnimator
+            android:duration="37"
+            android:propertyName="pathData"
+            android:valueFrom="M -4.36843359242,-1.49992262412 l 8.73686718484,0.0 c 0.0728757880921,0.0 0.131953286993,0.0590774989007 0.131953286993,0.131953286993 l 0.0,2.73593867425 c 0.0,0.0728757880921 -0.0590774989007,0.131953286993 -0.131953286993,0.131953286993 l -8.73686718484,0.0 c -0.0728757880921,0.0 -0.131953286993,-0.0590774989007 -0.131953286993,-0.131953286993 l 0.0,-2.73593867425 c 0.0,-0.0728757880921 0.0590774989007,-0.131953286993 0.131953286993,-0.131953286993 Z"
+            android:valueTo="M -2.7976112102,-1.69047775796 l 5.59522242041,0.0 c 0.41421356235,0.0 0.75,0.33578643765 0.75,0.75 l 0.0,1.88095551592 c 0.0,0.41421356235 -0.33578643765,0.75 -0.75,0.75 l -5.59522242041,0.0 c -0.41421356235,0.0 -0.75,-0.33578643765 -0.75,-0.75 l 0.0,-1.88095551592 c 0.0,-0.41421356235 0.33578643765,-0.75 0.75,-0.75 Z"
+            android:valueType="pathType"
+            android:interpolator="@android:interpolator/linear" />
+        <objectAnimator
+            android:duration="37"
+            android:propertyName="pathData"
+            android:valueFrom="M -2.7976112102,-1.69047775796 l 5.59522242041,0.0 c 0.41421356235,0.0 0.75,0.33578643765 0.75,0.75 l 0.0,1.88095551592 c 0.0,0.41421356235 -0.33578643765,0.75 -0.75,0.75 l -5.59522242041,0.0 c -0.41421356235,0.0 -0.75,-0.33578643765 -0.75,-0.75 l 0.0,-1.88095551592 c 0.0,-0.41421356235 0.33578643765,-0.75 0.75,-0.75 Z"
+            android:valueTo="M -1.5412962309,-1.81003891076 l 3.08259246181,0.0 c 0.777898159561,0.0 1.4085092153,0.630611055735 1.4085092153,1.4085092153 l 0.0,0.803059390927 c 0.0,0.777898159561 -0.630611055735,1.4085092153 -1.4085092153,1.4085092153 l -3.08259246181,0.0 c -0.777898159561,0.0 -1.4085092153,-0.630611055735 -1.4085092153,-1.4085092153 l 0.0,-0.803059390927 c 0.0,-0.777898159561 0.630611055735,-1.4085092153 1.4085092153,-1.4085092153 Z"
+            android:valueType="pathType"
+            android:interpolator="@android:interpolator/linear" />
+        <objectAnimator
+            android:duration="37"
+            android:propertyName="pathData"
+            android:valueFrom="M -1.5412962309,-1.81003891076 l 3.08259246181,0.0 c 0.777898159561,0.0 1.4085092153,0.630611055735 1.4085092153,1.4085092153 l 0.0,0.803059390927 c 0.0,0.777898159561 -0.630611055735,1.4085092153 -1.4085092153,1.4085092153 l -3.08259246181,0.0 c -0.777898159561,0.0 -1.4085092153,-0.630611055735 -1.4085092153,-1.4085092153 l 0.0,-0.803059390927 c 0.0,-0.777898159561 0.630611055735,-1.4085092153 1.4085092153,-1.4085092153 Z"
+            android:valueTo="M -0.798718330914,-1.88987363368 l 1.59743666183,0.0 c 0.967555109393,0.0 1.75191350068,0.784358391285 1.75191350068,1.75191350068 l 0.0,0.275920266008 c 0.0,0.967555109393 -0.784358391285,1.75191350068 -1.75191350068,1.75191350068 l -1.59743666183,0.0 c -0.967555109393,0.0 -1.75191350068,-0.784358391285 -1.75191350068,-1.75191350068 l 0.0,-0.275920266008 c 0.0,-0.967555109393 0.784358391285,-1.75191350068 1.75191350068,-1.75191350068 Z"
+            android:valueType="pathType"
+            android:interpolator="@android:interpolator/linear" />
+        <objectAnimator
+            android:duration="37"
+            android:propertyName="pathData"
+            android:valueFrom="M -0.798718330914,-1.88987363368 l 1.59743666183,0.0 c 0.967555109393,0.0 1.75191350068,0.784358391285 1.75191350068,1.75191350068 l 0.0,0.275920266008 c 0.0,0.967555109393 -0.784358391285,1.75191350068 -1.75191350068,1.75191350068 l -1.59743666183,0.0 c -0.967555109393,0.0 -1.75191350068,-0.784358391285 -1.75191350068,-1.75191350068 l 0.0,-0.275920266008 c 0.0,-0.967555109393 0.784358391285,-1.75191350068 1.75191350068,-1.75191350068 Z"
+            android:valueTo="M -0.366220962052,-1.94300934217 l 0.732441924103,0.0 c 1.05968660322,0.0 1.91873232712,0.859045723904 1.91873232712,1.91873232712 l 0.0,0.0485540300878 c 0.0,1.05968660322 -0.859045723904,1.91873232712 -1.91873232712,1.91873232712 l -0.732441924103,0.0 c -1.05968660322,0.0 -1.91873232712,-0.859045723904 -1.91873232712,-1.91873232712 l 0.0,-0.0485540300878 c 0.0,-1.05968660322 0.859045723904,-1.91873232712 1.91873232712,-1.91873232712 Z"
+            android:valueType="pathType"
+            android:interpolator="@android:interpolator/linear" />
+        <objectAnimator
+            android:duration="37"
+            android:propertyName="pathData"
+            android:valueFrom="M -0.366220962052,-1.94300934217 l 0.732441924103,0.0 c 1.05968660322,0.0 1.91873232712,0.859045723904 1.91873232712,1.91873232712 l 0.0,0.0485540300878 c 0.0,1.05968660322 -0.859045723904,1.91873232712 -1.91873232712,1.91873232712 l -0.732441924103,0.0 c -1.05968660322,0.0 -1.91873232712,-0.859045723904 -1.91873232712,-1.91873232712 l 0.0,-0.0485540300878 c 0.0,-1.05968660322 0.859045723904,-1.91873232712 1.91873232712,-1.91873232712 Z"
+            android:valueTo="M -0.141334109858,-1.97644431502 l 0.282668219716,0.0 c 1.09156005402,0.0 1.97644431502,0.884884261007 1.97644431502,1.97644431502 l 0.0,0.0 c 0.0,1.09156005402 -0.884884261007,1.97644431502 -1.97644431502,1.97644431502 l -0.282668219716,0.0 c -1.09156005402,0.0 -1.97644431502,-0.884884261007 -1.97644431502,-1.97644431502 l 0.0,0.0 c 0.0,-1.09156005402 0.884884261007,-1.97644431502 1.97644431502,-1.97644431502 Z"
+            android:valueType="pathType"
+            android:interpolator="@android:interpolator/linear" />
+        <objectAnimator
+            android:duration="37"
+            android:propertyName="pathData"
+            android:valueFrom="M -0.141334109858,-1.97644431502 l 0.282668219716,0.0 c 1.09156005402,0.0 1.97644431502,0.884884261007 1.97644431502,1.97644431502 l 0.0,0.0 c 0.0,1.09156005402 -0.884884261007,1.97644431502 -1.97644431502,1.97644431502 l -0.282668219716,0.0 c -1.09156005402,0.0 -1.97644431502,-0.884884261007 -1.97644431502,-1.97644431502 l 0.0,0.0 c 0.0,-1.09156005402 0.884884261007,-1.97644431502 1.97644431502,-1.97644431502 Z"
+            android:valueTo="M -0.0331287849506,-1.99447853584 l 0.0662575699012,0.0 c 1.10152007915,0.0 1.99447853584,0.892958456693 1.99447853584,1.99447853584 l 0.0,0.0 c 0.0,1.10152007915 -0.892958456693,1.99447853584 -1.99447853584,1.99447853584 l -0.0662575699012,0.0 c -1.10152007915,0.0 -1.99447853584,-0.892958456693 -1.99447853584,-1.99447853584 l 0.0,0.0 c 0.0,-1.10152007915 0.892958456693,-1.99447853584 1.99447853584,-1.99447853584 Z"
+            android:valueType="pathType"
+            android:interpolator="@android:interpolator/linear" />
+        <objectAnimator
+            android:duration="37"
+            android:propertyName="pathData"
+            android:valueFrom="M -0.0331287849506,-1.99447853584 l 0.0662575699012,0.0 c 1.10152007915,0.0 1.99447853584,0.892958456693 1.99447853584,1.99447853584 l 0.0,0.0 c 0.0,1.10152007915 -0.892958456693,1.99447853584 -1.99447853584,1.99447853584 l -0.0662575699012,0.0 c -1.10152007915,0.0 -1.99447853584,-0.892958456693 -1.99447853584,-1.99447853584 l 0.0,0.0 c 0.0,-1.10152007915 0.892958456693,-1.99447853584 1.99447853584,-1.99447853584 Z"
+            android:valueTo="M 0.0,-2.0 l 0.0,0.0 c 1.1045694996,0.0 2.0,0.8954305004 2.0,2.0 l 0.0,0.0 c 0.0,1.1045694996 -0.8954305004,2.0 -2.0,2.0 l 0.0,0.0 c -1.1045694996,0.0 -2.0,-0.8954305004 -2.0,-2.0 l 0.0,0.0 c 0.0,-1.1045694996 0.8954305004,-2.0 2.0,-2.0 Z"
+            android:valueType="pathType"
+            android:interpolator="@android:interpolator/linear" />
+    </set>
+</set>
diff --git a/core/res/res/drawable/ft_avd_toarrow.xml b/core/res/res/drawable/ft_avd_toarrow.xml
new file mode 100644
index 0000000..087ea74
--- /dev/null
+++ b/core/res/res/drawable/ft_avd_toarrow.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You 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.
+*/
+-->
+<vector
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:name="ft_avd_toarrow"
+    android:width="24dp"
+    android:viewportWidth="24"
+    android:height="24dp"
+    android:viewportHeight="24" >
+    <group
+        android:name="carrot_3"
+        android:translateX="12"
+        android:translateY="12" >
+        <group
+            android:name="rectangle_4"
+            android:translateY="6" >
+            <group
+                android:name="rectangle_3_pivot_0" >
+                <path
+                    android:name="rectangle_path_4"
+                    android:fillColor="#FF000000"
+                    android:pathData="M 0.0,-2.0 l 0.0,0.0 c 1.1045694996,0.0 2.0,0.8954305004 2.0,2.0 l 0.0,0.0 c 0.0,1.1045694996 -0.8954305004,2.0 -2.0,2.0 l 0.0,0.0 c -1.1045694996,0.0 -2.0,-0.8954305004 -2.0,-2.0 l 0.0,0.0 c 0.0,-1.1045694996 0.8954305004,-2.0 2.0,-2.0 Z" />
+            </group>
+        </group>
+        <group
+            android:name="rectangle_5" >
+            <group
+                android:name="rectangle_2_pivot_0" >
+                <path
+                    android:name="rectangle_path_5"
+                    android:fillColor="#FF000000"
+                    android:pathData="M 0.0,-2.0 l 0.0,0.0 c 1.1045694996,0.0 2.0,0.8954305004 2.0,2.0 l 0.0,0.0 c 0.0,1.1045694996 -0.8954305004,2.0 -2.0,2.0 l 0.0,0.0 c -1.1045694996,0.0 -2.0,-0.8954305004 -2.0,-2.0 l 0.0,0.0 c 0.0,-1.1045694996 0.8954305004,-2.0 2.0,-2.0 Z" />
+            </group>
+        </group>
+        <group
+            android:name="rectangle_6"
+            android:translateY="-6" >
+            <group
+                android:name="rectangle_1_pivot_0" >
+                <path
+                    android:name="rectangle_path_6"
+                    android:fillColor="#FF000000"
+                    android:pathData="M 0.0,-2.0 l 0.0,0.0 c 1.1045694996,0.0 2.0,0.8954305004 2.0,2.0 l 0.0,0.0 c 0.0,1.1045694996 -0.8954305004,2.0 -2.0,2.0 l 0.0,0.0 c -1.1045694996,0.0 -2.0,-0.8954305004 -2.0,-2.0 l 0.0,0.0 c 0.0,-1.1045694996 0.8954305004,-2.0 2.0,-2.0 Z" />
+            </group>
+        </group>
+    </group>
+</vector>
diff --git a/core/res/res/drawable/ft_avd_toarrow_animation.xml b/core/res/res/drawable/ft_avd_toarrow_animation.xml
new file mode 100644
index 0000000..e31067d
--- /dev/null
+++ b/core/res/res/drawable/ft_avd_toarrow_animation.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You 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.
+*/
+-->
+<animated-vector
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:drawable="@drawable/ft_avd_toarrow" >
+    <target
+        android:name="rectangle_4"
+        android:animation="@anim/ft_avd_toarrow_rectangle_4_animation" />
+    <target
+        android:name="rectangle_3_pivot_0"
+        android:animation="@anim/ft_avd_toarrow_rectangle_3_pivot_0_animation" />
+    <target
+        android:name="rectangle_path_4"
+        android:animation="@anim/ft_avd_toarrow_rectangle_path_4_animation" />
+    <target
+        android:name="rectangle_5"
+        android:animation="@anim/ft_avd_toarrow_rectangle_5_animation" />
+    <target
+        android:name="rectangle_2_pivot_0"
+        android:animation="@anim/ft_avd_toarrow_rectangle_2_pivot_0_animation" />
+    <target
+        android:name="rectangle_path_5"
+        android:animation="@anim/ft_avd_toarrow_rectangle_path_5_animation" />
+    <target
+        android:name="rectangle_6"
+        android:animation="@anim/ft_avd_toarrow_rectangle_6_animation" />
+    <target
+        android:name="rectangle_1_pivot_0"
+        android:animation="@anim/ft_avd_toarrow_rectangle_1_pivot_0_animation" />
+    <target
+        android:name="rectangle_path_6"
+        android:animation="@anim/ft_avd_toarrow_rectangle_path_6_animation" />
+</animated-vector>
diff --git a/core/res/res/drawable/ft_avd_tooverflow.xml b/core/res/res/drawable/ft_avd_tooverflow.xml
new file mode 100644
index 0000000..a2b9cec
--- /dev/null
+++ b/core/res/res/drawable/ft_avd_tooverflow.xml
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You 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.
+*/
+-->
+<vector
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:name="ft_avd_tooverflow"
+    android:width="24dp"
+    android:viewportWidth="24"
+    android:height="24dp"
+    android:viewportHeight="24" >
+    <group
+        android:name="carrot_5"
+        android:translateX="12"
+        android:translateY="12" >
+        <group
+            android:name="rectangle_3"
+            android:translateX="-6.5"
+            android:rotation="45" >
+            <group
+                android:name="rectangle_3_pivot"
+                android:translateX="4.5" >
+                <path
+                    android:name="rectangle_path_2"
+                    android:fillColor="#FF000000"
+                    android:pathData="M -5.5,-1.0 l 11.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l -11.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z" />
+            </group>
+        </group>
+        <group
+            android:name="rectangle_2"
+            android:translateX="-8" >
+            <group
+                android:name="rectangle_2_pivot"
+                android:translateX="9" >
+                <path
+                    android:name="rectangle_path_3"
+                    android:fillColor="#FF000000"
+                    android:pathData="M -7.0,-1.0 l 14.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l -14.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z" />
+            </group>
+        </group>
+        <group
+            android:name="rectangle_1"
+            android:translateX="-6.5"
+            android:rotation="-45" >
+            <group
+                android:name="rectangle_1_pivot"
+                android:translateX="4.5" >
+                <path
+                    android:name="rectangle_path_1"
+                    android:fillColor="#FF000000"
+                    android:pathData="M -5.5,-1.0 l 11.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l -11.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z" />
+            </group>
+        </group>
+    </group>
+</vector>
diff --git a/core/res/res/drawable/ft_avd_tooverflow_animation.xml b/core/res/res/drawable/ft_avd_tooverflow_animation.xml
new file mode 100644
index 0000000..ed9975b
--- /dev/null
+++ b/core/res/res/drawable/ft_avd_tooverflow_animation.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You 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.
+*/
+-->
+<animated-vector
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:drawable="@drawable/ft_avd_tooverflow" >
+    <target
+        android:name="rectangle_3"
+        android:animation="@anim/ft_avd_tooverflow_rectangle_3_animation" />
+    <target
+        android:name="rectangle_3_pivot"
+        android:animation="@anim/ft_avd_tooverflow_rectangle_3_pivot_animation" />
+    <target
+        android:name="rectangle_path_2"
+        android:animation="@anim/ft_avd_tooverflow_rectangle_path_2_animation" />
+    <target
+        android:name="rectangle_2"
+        android:animation="@anim/ft_avd_tooverflow_rectangle_2_animation" />
+    <target
+        android:name="rectangle_2_pivot"
+        android:animation="@anim/ft_avd_tooverflow_rectangle_2_pivot_animation" />
+    <target
+        android:name="rectangle_path_3"
+        android:animation="@anim/ft_avd_tooverflow_rectangle_path_3_animation" />
+    <target
+        android:name="rectangle_1"
+        android:animation="@anim/ft_avd_tooverflow_rectangle_1_animation" />
+    <target
+        android:name="rectangle_1_pivot"
+        android:animation="@anim/ft_avd_tooverflow_rectangle_1_pivot_animation" />
+    <target
+        android:name="rectangle_path_1"
+        android:animation="@anim/ft_avd_tooverflow_rectangle_path_1_animation" />
+</animated-vector>
diff --git a/core/res/res/interpolator/ft_avd_toarrow_animation_interpolator_0.xml b/core/res/res/interpolator/ft_avd_toarrow_animation_interpolator_0.xml
new file mode 100644
index 0000000..c6db901
--- /dev/null
+++ b/core/res/res/interpolator/ft_avd_toarrow_animation_interpolator_0.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You 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.
+*/
+-->
+<pathInterpolator
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:pathData="M 0.0,0.0 l 0.668316831683,0.0 c 0.00003,0.0 0.0663366336634,1.0 0.331683168317,1.0 L 1.0,1.0" />
diff --git a/core/res/res/interpolator/ft_avd_toarrow_animation_interpolator_1.xml b/core/res/res/interpolator/ft_avd_toarrow_animation_interpolator_1.xml
new file mode 100644
index 0000000..584385d
--- /dev/null
+++ b/core/res/res/interpolator/ft_avd_toarrow_animation_interpolator_1.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You 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.
+*/
+-->
+<pathInterpolator
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:pathData="M 0.0,0.0 c 0.0001,0.0 0.0,1.0 1.0,1.0" />
diff --git a/core/res/res/interpolator/ft_avd_toarrow_animation_interpolator_2.xml b/core/res/res/interpolator/ft_avd_toarrow_animation_interpolator_2.xml
new file mode 100644
index 0000000..334dee7
--- /dev/null
+++ b/core/res/res/interpolator/ft_avd_toarrow_animation_interpolator_2.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You 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.
+*/
+-->
+<pathInterpolator
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:pathData="M 0.0,0.0 c 0.161290322581,0.0 0.0806451612903,0.909090909091 0.403225806452,0.909090909091 c 0.238709677419,0.0 0.11935483871,0.0909090909091 0.596774193548,0.0909090909091 L 1.0,1.0" />
diff --git a/core/res/res/interpolator/ft_avd_toarrow_animation_interpolator_3.xml b/core/res/res/interpolator/ft_avd_toarrow_animation_interpolator_3.xml
new file mode 100644
index 0000000..67891fc
--- /dev/null
+++ b/core/res/res/interpolator/ft_avd_toarrow_animation_interpolator_3.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You 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.
+*/
+-->
+<pathInterpolator
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:pathData="M 0.0,0.0 l 0.5,0.0 c 0.2,0.0 0.1,1.0 0.5,1.0 L 1.0,1.0" />
diff --git a/core/res/res/interpolator/ft_avd_toarrow_animation_interpolator_4.xml b/core/res/res/interpolator/ft_avd_toarrow_animation_interpolator_4.xml
new file mode 100644
index 0000000..756a9e1
--- /dev/null
+++ b/core/res/res/interpolator/ft_avd_toarrow_animation_interpolator_4.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You 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.
+*/
+-->
+<pathInterpolator
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:pathData="M 0.0,0.0 l 0.5,0.0 c 0.00005,0.0 0.1,1.0 0.5,1.0 L 1.0,1.0" />
diff --git a/core/res/res/interpolator/ft_avd_toarrow_animation_interpolator_5.xml b/core/res/res/interpolator/ft_avd_toarrow_animation_interpolator_5.xml
new file mode 100644
index 0000000..584385d
--- /dev/null
+++ b/core/res/res/interpolator/ft_avd_toarrow_animation_interpolator_5.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You 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.
+*/
+-->
+<pathInterpolator
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:pathData="M 0.0,0.0 c 0.0001,0.0 0.0,1.0 1.0,1.0" />
diff --git a/core/res/res/interpolator/ft_avd_toarrow_animation_interpolator_6.xml b/core/res/res/interpolator/ft_avd_toarrow_animation_interpolator_6.xml
new file mode 100644
index 0000000..bed54d4
--- /dev/null
+++ b/core/res/res/interpolator/ft_avd_toarrow_animation_interpolator_6.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You 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.
+*/
+-->
+<pathInterpolator
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:pathData="M 0.0,0.0 l 0.582706766917,0.0 c 0.166917293233,0.0 0.0834586466165,1.0 0.417293233083,1.0 L 1.0,1.0" />
diff --git a/core/res/res/layout/floating_popup_container.xml b/core/res/res/layout/floating_popup_container.xml
index 63dae44..dd161e3 100644
--- a/core/res/res/layout/floating_popup_container.xml
+++ b/core/res/res/layout/floating_popup_container.xml
@@ -15,12 +15,11 @@
 ** limitations under the License.
 */
 -->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:orientation="horizontal"
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="wrap_content"
-    android:layout_height="@dimen/floating_toolbar_height"
+    android:layout_height="wrap_content"
     android:padding="0dp"
-    android:layout_margin="0dp"
+    android:layout_margin="20dp"
     android:elevation="2dp"
     android:focusable="true"
     android:focusableInTouchMode="true"
diff --git a/core/res/res/layout/floating_popup_overflow_button.xml b/core/res/res/layout/floating_popup_overflow_button.xml
new file mode 100644
index 0000000..7053f3e
--- /dev/null
+++ b/core/res/res/layout/floating_popup_overflow_button.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You 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.
+*/
+-->
+<ImageButton xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/overflow"
+    android:layout_width="@dimen/floating_toolbar_menu_image_button_width"
+    android:layout_height="@dimen/floating_toolbar_height"
+    android:paddingStart="@dimen/floating_toolbar_menu_button_side_padding"
+    android:paddingTop="@dimen/floating_toolbar_menu_image_button_vertical_padding"
+    android:paddingEnd="@dimen/floating_toolbar_menu_button_side_padding"
+    android:paddingBottom="@dimen/floating_toolbar_menu_image_button_vertical_padding"
+    android:scaleType="centerInside"
+    android:background="?attr/selectableItemBackgroundBorderless"
+    android:tint="?attr/floatingToolbarForegroundColor" />
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 669eba1..c3f7afc 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -222,6 +222,8 @@
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Stembystand"</string>
     <string name="global_action_lockdown" msgid="8751542514724332873">"Sluit nou"</string>
     <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
+    <!-- no translation found for notification_children_count_bracketed (1769425473168347839) -->
+    <skip />
     <string name="safeMode" msgid="2788228061547930246">"Veiligmodus"</string>
     <string name="android_system_label" msgid="6577375335728551336">"Android-stelsel"</string>
     <string name="user_owner_label" msgid="2804351898001038951">"Persoonlik"</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index a7af0f1..4de0097 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -222,6 +222,8 @@
     <string name="global_action_voice_assist" msgid="7751191495200504480">"የድምጽ እርዳታ"</string>
     <string name="global_action_lockdown" msgid="8751542514724332873">"አሁን ቆልፍ"</string>
     <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
+    <!-- no translation found for notification_children_count_bracketed (1769425473168347839) -->
+    <skip />
     <string name="safeMode" msgid="2788228061547930246">"የሚያስተማምን ሁነታ"</string>
     <string name="android_system_label" msgid="6577375335728551336">"Android ስርዓት"</string>
     <string name="user_owner_label" msgid="2804351898001038951">"የግል"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 9be3bfb..0c05bc4 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -226,6 +226,8 @@
     <string name="global_action_voice_assist" msgid="7751191495200504480">"المساعد الصوتي"</string>
     <string name="global_action_lockdown" msgid="8751542514724332873">"قفل الآن"</string>
     <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
+    <!-- no translation found for notification_children_count_bracketed (1769425473168347839) -->
+    <skip />
     <string name="safeMode" msgid="2788228061547930246">"الوضع الآمن"</string>
     <string name="android_system_label" msgid="6577375335728551336">"‏نظام Android"</string>
     <string name="user_owner_label" msgid="2804351898001038951">"شخصي"</string>
diff --git a/core/res/res/values-az-rAZ/strings.xml b/core/res/res/values-az-rAZ/strings.xml
index 54dbc39e..593400e 100644
--- a/core/res/res/values-az-rAZ/strings.xml
+++ b/core/res/res/values-az-rAZ/strings.xml
@@ -222,6 +222,7 @@
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Səs Yardımçısı"</string>
     <string name="global_action_lockdown" msgid="8751542514724332873">"İndi kilidləyin"</string>
     <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
+    <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g>)"</string>
     <string name="safeMode" msgid="2788228061547930246">"Təhlükəsiz rejim"</string>
     <string name="android_system_label" msgid="6577375335728551336">"Android sistemi"</string>
     <string name="user_owner_label" msgid="2804351898001038951">"Şəxsi"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 2617bdb..50f2938 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -222,6 +222,8 @@
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Гласова помощ"</string>
     <string name="global_action_lockdown" msgid="8751542514724332873">"Заключване сега"</string>
     <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
+    <!-- no translation found for notification_children_count_bracketed (1769425473168347839) -->
+    <skip />
     <string name="safeMode" msgid="2788228061547930246">"Безопасен режим"</string>
     <string name="android_system_label" msgid="6577375335728551336">"Системно от Android"</string>
     <string name="user_owner_label" msgid="2804351898001038951">"Личен"</string>
diff --git a/core/res/res/values-bn-rBD/strings.xml b/core/res/res/values-bn-rBD/strings.xml
index f5fb017..fdd46172 100644
--- a/core/res/res/values-bn-rBD/strings.xml
+++ b/core/res/res/values-bn-rBD/strings.xml
@@ -222,6 +222,8 @@
     <string name="global_action_voice_assist" msgid="7751191495200504480">"ভয়েস সহায়তা"</string>
     <string name="global_action_lockdown" msgid="8751542514724332873">"এখনই লক করুন"</string>
     <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"৯৯৯+"</string>
+    <!-- no translation found for notification_children_count_bracketed (1769425473168347839) -->
+    <skip />
     <string name="safeMode" msgid="2788228061547930246">"নিরাপদ মোড"</string>
     <string name="android_system_label" msgid="6577375335728551336">"Android সিস্টেম"</string>
     <string name="user_owner_label" msgid="2804351898001038951">"ব্যক্তিগত"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 0e731f6..cb24d26 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -222,6 +222,8 @@
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Assist. per veu"</string>
     <string name="global_action_lockdown" msgid="8751542514724332873">"Bloqueja ara"</string>
     <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"+999"</string>
+    <!-- no translation found for notification_children_count_bracketed (1769425473168347839) -->
+    <skip />
     <string name="safeMode" msgid="2788228061547930246">"Mode segur"</string>
     <string name="android_system_label" msgid="6577375335728551336">"Sistema Android"</string>
     <string name="user_owner_label" msgid="2804351898001038951">"Personal"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index fd3ed4b..c2f1364 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -224,6 +224,8 @@
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Hlas. asistence"</string>
     <string name="global_action_lockdown" msgid="8751542514724332873">"Zamknout"</string>
     <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
+    <!-- no translation found for notification_children_count_bracketed (1769425473168347839) -->
+    <skip />
     <string name="safeMode" msgid="2788228061547930246">"Nouzový režim"</string>
     <string name="android_system_label" msgid="6577375335728551336">"Systém Android"</string>
     <string name="user_owner_label" msgid="2804351898001038951">"Osobní"</string>
@@ -355,7 +357,7 @@
     <string name="permlab_vibrate" msgid="7696427026057705834">"ovládání vibrací"</string>
     <string name="permdesc_vibrate" msgid="6284989245902300945">"Umožňuje aplikaci ovládat vibrace."</string>
     <string name="permlab_flashlight" msgid="2155920810121984215">"ovládání kontrolky"</string>
-    <string name="permdesc_flashlight" msgid="6522284794568368310">"Umožňuje aplikaci ovládat baterku."</string>
+    <string name="permdesc_flashlight" msgid="6522284794568368310">"Umožňuje aplikaci ovládat svítilnu."</string>
     <string name="permlab_callPhone" msgid="3925836347681847954">"přímé volání na telefonní čísla"</string>
     <string name="permdesc_callPhone" msgid="3740797576113760827">"Umožňuje aplikaci volat na telefonní čísla bez vašeho přičinění. Může mít za následek neočekávané poplatky nebo hovory. Toto oprávnění neumožňuje aplikaci volat na tísňová čísla. Škodlivé aplikace vás mohou připravit o peníze uskutečňováním hovorů bez vašeho svolení."</string>
     <string name="permlab_accessImsCallService" msgid="3574943847181793918">"přístup ke službě zasílání rychlých zpráv pro účely hovorů"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index dbf85e3..42cb42b 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -222,6 +222,7 @@
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Taleassistent"</string>
     <string name="global_action_lockdown" msgid="8751542514724332873">"Lås nu"</string>
     <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
+    <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g>)"</string>
     <string name="safeMode" msgid="2788228061547930246">"Sikker tilstand"</string>
     <string name="android_system_label" msgid="6577375335728551336">"Android-system"</string>
     <string name="user_owner_label" msgid="2804351898001038951">"Personlig"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index f8b78c0..d52c728 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -222,6 +222,8 @@
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Sprachassistent"</string>
     <string name="global_action_lockdown" msgid="8751542514724332873">"Jetzt sperren"</string>
     <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
+    <!-- no translation found for notification_children_count_bracketed (1769425473168347839) -->
+    <skip />
     <string name="safeMode" msgid="2788228061547930246">"Abgesicherter Modus"</string>
     <string name="android_system_label" msgid="6577375335728551336">"Android-System"</string>
     <string name="user_owner_label" msgid="2804351898001038951">"Privat"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index e00fbec..f86c2fd 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -222,6 +222,8 @@
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Φων.υποβοηθ."</string>
     <string name="global_action_lockdown" msgid="8751542514724332873">"Κλείδωμα τώρα"</string>
     <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
+    <!-- no translation found for notification_children_count_bracketed (1769425473168347839) -->
+    <skip />
     <string name="safeMode" msgid="2788228061547930246">"Ασφαλής λειτουργία"</string>
     <string name="android_system_label" msgid="6577375335728551336">"Σύστημα Android"</string>
     <string name="user_owner_label" msgid="2804351898001038951">"Προσωπικό"</string>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index c66c55a..e23f8d5 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -222,6 +222,7 @@
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Voice Assist"</string>
     <string name="global_action_lockdown" msgid="8751542514724332873">"Lock now"</string>
     <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
+    <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g>)"</string>
     <string name="safeMode" msgid="2788228061547930246">"Safe mode"</string>
     <string name="android_system_label" msgid="6577375335728551336">"Android system"</string>
     <string name="user_owner_label" msgid="2804351898001038951">"Personal"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index c66c55a..e23f8d5 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -222,6 +222,7 @@
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Voice Assist"</string>
     <string name="global_action_lockdown" msgid="8751542514724332873">"Lock now"</string>
     <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
+    <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g>)"</string>
     <string name="safeMode" msgid="2788228061547930246">"Safe mode"</string>
     <string name="android_system_label" msgid="6577375335728551336">"Android system"</string>
     <string name="user_owner_label" msgid="2804351898001038951">"Personal"</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index c66c55a..e23f8d5 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -222,6 +222,7 @@
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Voice Assist"</string>
     <string name="global_action_lockdown" msgid="8751542514724332873">"Lock now"</string>
     <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
+    <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g>)"</string>
     <string name="safeMode" msgid="2788228061547930246">"Safe mode"</string>
     <string name="android_system_label" msgid="6577375335728551336">"Android system"</string>
     <string name="user_owner_label" msgid="2804351898001038951">"Personal"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 19f1bd9..b442bc1f 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -222,6 +222,8 @@
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Asistente voz"</string>
     <string name="global_action_lockdown" msgid="8751542514724332873">"Bloquear ahora"</string>
     <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
+    <!-- no translation found for notification_children_count_bracketed (1769425473168347839) -->
+    <skip />
     <string name="safeMode" msgid="2788228061547930246">"Modo seguro"</string>
     <string name="android_system_label" msgid="6577375335728551336">"Sistema Android"</string>
     <string name="user_owner_label" msgid="2804351898001038951">"Personal"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index f290c04..acbef23 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -222,6 +222,8 @@
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Asistente voz"</string>
     <string name="global_action_lockdown" msgid="8751542514724332873">"Bloquear ahora"</string>
     <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"&gt; 999"</string>
+    <!-- no translation found for notification_children_count_bracketed (1769425473168347839) -->
+    <skip />
     <string name="safeMode" msgid="2788228061547930246">"Modo seguro"</string>
     <string name="android_system_label" msgid="6577375335728551336">"Sistema Android"</string>
     <string name="user_owner_label" msgid="2804351898001038951">"Personal"</string>
@@ -243,7 +245,7 @@
     <string name="permgrouplab_phone" msgid="5229115638567440675">"Teléfono"</string>
     <string name="permgroupdesc_phone" msgid="6234224354060641055">"hacer y administrar llamadas de teléfono"</string>
     <string name="permgrouplab_sensors" msgid="416037179223226722">"Sensores corporales"</string>
-    <string name="permgroupdesc_sensors" msgid="7147968539346634043">"acceder a datos del sensor sobre tus constantes vitales"</string>
+    <string name="permgroupdesc_sensors" msgid="7147968539346634043">"acceder a datos de sensores de tus constantes vitales"</string>
     <string name="capability_title_canRetrieveWindowContent" msgid="3901717936930170320">"Recuperar el contenido de la ventana"</string>
     <string name="capability_desc_canRetrieveWindowContent" msgid="3772225008605310672">"Inspecciona el contenido de una ventana con la que estés interactuando."</string>
     <string name="capability_title_canRequestTouchExploration" msgid="3108723364676667320">"Activar la exploración táctil"</string>
diff --git a/core/res/res/values-et-rEE/strings.xml b/core/res/res/values-et-rEE/strings.xml
index ca6ad3c..9a96683 100644
--- a/core/res/res/values-et-rEE/strings.xml
+++ b/core/res/res/values-et-rEE/strings.xml
@@ -222,6 +222,8 @@
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Häälabi"</string>
     <string name="global_action_lockdown" msgid="8751542514724332873">"Lukusta kohe"</string>
     <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
+    <!-- no translation found for notification_children_count_bracketed (1769425473168347839) -->
+    <skip />
     <string name="safeMode" msgid="2788228061547930246">"Turvarežiim"</string>
     <string name="android_system_label" msgid="6577375335728551336">"Android-süsteem"</string>
     <string name="user_owner_label" msgid="2804351898001038951">"Isiklik"</string>
diff --git a/core/res/res/values-eu-rES/strings.xml b/core/res/res/values-eu-rES/strings.xml
index 0986b70..a30c437 100644
--- a/core/res/res/values-eu-rES/strings.xml
+++ b/core/res/res/values-eu-rES/strings.xml
@@ -222,6 +222,8 @@
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Ahots-laguntza"</string>
     <string name="global_action_lockdown" msgid="8751542514724332873">"Blokeatu"</string>
     <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
+    <!-- no translation found for notification_children_count_bracketed (1769425473168347839) -->
+    <skip />
     <string name="safeMode" msgid="2788228061547930246">"Modu segurua"</string>
     <string name="android_system_label" msgid="6577375335728551336">"Android sistema"</string>
     <string name="user_owner_label" msgid="2804351898001038951">"Pertsonalak"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index bc5caa2..8672171 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -222,6 +222,8 @@
     <string name="global_action_voice_assist" msgid="7751191495200504480">"دستیار صوتی"</string>
     <string name="global_action_lockdown" msgid="8751542514724332873">"اکنون قفل شود"</string>
     <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"بیشتر از 999"</string>
+    <!-- no translation found for notification_children_count_bracketed (1769425473168347839) -->
+    <skip />
     <string name="safeMode" msgid="2788228061547930246">"حالت ایمن"</string>
     <string name="android_system_label" msgid="6577375335728551336">"‏سیستم Android"</string>
     <string name="user_owner_label" msgid="2804351898001038951">"شخصی"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 3494d53..8100305 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -222,6 +222,8 @@
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Ääniapuri"</string>
     <string name="global_action_lockdown" msgid="8751542514724332873">"Lukitse nyt"</string>
     <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
+    <!-- no translation found for notification_children_count_bracketed (1769425473168347839) -->
+    <skip />
     <string name="safeMode" msgid="2788228061547930246">"Suojattu tila"</string>
     <string name="android_system_label" msgid="6577375335728551336">"Android-järjestelmä"</string>
     <string name="user_owner_label" msgid="2804351898001038951">"Henkilökoht."</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index 94f59fa..b273980 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -222,6 +222,8 @@
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Assist. vocale"</string>
     <string name="global_action_lockdown" msgid="8751542514724332873">"Verrouiller"</string>
     <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"&gt;999"</string>
+    <!-- no translation found for notification_children_count_bracketed (1769425473168347839) -->
+    <skip />
     <string name="safeMode" msgid="2788228061547930246">"Mode sécurisé"</string>
     <string name="android_system_label" msgid="6577375335728551336">"Système Android"</string>
     <string name="user_owner_label" msgid="2804351898001038951">"Personnel"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 965b229..850c18a 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -222,6 +222,8 @@
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Assistance vocale"</string>
     <string name="global_action_lockdown" msgid="8751542514724332873">"Verrouiller"</string>
     <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"&gt;999"</string>
+    <!-- no translation found for notification_children_count_bracketed (1769425473168347839) -->
+    <skip />
     <string name="safeMode" msgid="2788228061547930246">"Mode sécurisé"</string>
     <string name="android_system_label" msgid="6577375335728551336">"Système Android"</string>
     <string name="user_owner_label" msgid="2804351898001038951">"Personnel"</string>
diff --git a/core/res/res/values-gl-rES/strings.xml b/core/res/res/values-gl-rES/strings.xml
index a5c42e3..998f494 100644
--- a/core/res/res/values-gl-rES/strings.xml
+++ b/core/res/res/values-gl-rES/strings.xml
@@ -222,6 +222,8 @@
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Asistente voz"</string>
     <string name="global_action_lockdown" msgid="8751542514724332873">"Bloquear agora"</string>
     <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"&gt;999"</string>
+    <!-- no translation found for notification_children_count_bracketed (1769425473168347839) -->
+    <skip />
     <string name="safeMode" msgid="2788228061547930246">"Modo seguro"</string>
     <string name="android_system_label" msgid="6577375335728551336">"Sistema Android"</string>
     <string name="user_owner_label" msgid="2804351898001038951">"Persoal"</string>
diff --git a/core/res/res/values-gu-rIN/strings.xml b/core/res/res/values-gu-rIN/strings.xml
index 6dabd69..7cfd200 100644
--- a/core/res/res/values-gu-rIN/strings.xml
+++ b/core/res/res/values-gu-rIN/strings.xml
@@ -222,6 +222,8 @@
     <string name="global_action_voice_assist" msgid="7751191495200504480">"વૉઇસ સહાય"</string>
     <string name="global_action_lockdown" msgid="8751542514724332873">"હવે લૉક કરો"</string>
     <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
+    <!-- no translation found for notification_children_count_bracketed (1769425473168347839) -->
+    <skip />
     <string name="safeMode" msgid="2788228061547930246">"સુરક્ષિત મોડ"</string>
     <string name="android_system_label" msgid="6577375335728551336">"Android સિસ્ટમ"</string>
     <string name="user_owner_label" msgid="2804351898001038951">"વ્યક્તિગત"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 47fdd28..a001211 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -222,6 +222,8 @@
     <string name="global_action_voice_assist" msgid="7751191495200504480">"वॉइस सहायक"</string>
     <string name="global_action_lockdown" msgid="8751542514724332873">"अभी लॉक करें"</string>
     <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
+    <!-- no translation found for notification_children_count_bracketed (1769425473168347839) -->
+    <skip />
     <string name="safeMode" msgid="2788228061547930246">"सुरक्षित मोड"</string>
     <string name="android_system_label" msgid="6577375335728551336">"Android सिस्‍टम"</string>
     <string name="user_owner_label" msgid="2804351898001038951">"व्यक्तिगत"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 2596f1c..55399af 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -223,6 +223,8 @@
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Glasovna pomoć"</string>
     <string name="global_action_lockdown" msgid="8751542514724332873">"Zaključaj sada"</string>
     <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
+    <!-- no translation found for notification_children_count_bracketed (1769425473168347839) -->
+    <skip />
     <string name="safeMode" msgid="2788228061547930246">"Siguran način rada"</string>
     <string name="android_system_label" msgid="6577375335728551336">"Sustav Android"</string>
     <string name="user_owner_label" msgid="2804351898001038951">"Osobno"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 1c0257f..dc87943 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -222,6 +222,8 @@
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Hangsegéd"</string>
     <string name="global_action_lockdown" msgid="8751542514724332873">"Zárolás most"</string>
     <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
+    <!-- no translation found for notification_children_count_bracketed (1769425473168347839) -->
+    <skip />
     <string name="safeMode" msgid="2788228061547930246">"Biztonsági üzemmód"</string>
     <string name="android_system_label" msgid="6577375335728551336">"Android rendszer"</string>
     <string name="user_owner_label" msgid="2804351898001038951">"Személyes"</string>
diff --git a/core/res/res/values-hy-rAM/strings.xml b/core/res/res/values-hy-rAM/strings.xml
index 29474f0..f1d32d8 100644
--- a/core/res/res/values-hy-rAM/strings.xml
+++ b/core/res/res/values-hy-rAM/strings.xml
@@ -222,6 +222,8 @@
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Ձայնային օգնութ"</string>
     <string name="global_action_lockdown" msgid="8751542514724332873">"Կողպել հիմա"</string>
     <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
+    <!-- no translation found for notification_children_count_bracketed (1769425473168347839) -->
+    <skip />
     <string name="safeMode" msgid="2788228061547930246">"Անվտանգ ռեժիմ"</string>
     <string name="android_system_label" msgid="6577375335728551336">"Android համակարգ"</string>
     <string name="user_owner_label" msgid="2804351898001038951">"Անձնական"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 9a3ae05..8e18dae 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -222,6 +222,8 @@
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Bantuan Suara"</string>
     <string name="global_action_lockdown" msgid="8751542514724332873">"Kunci sekarang"</string>
     <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
+    <!-- no translation found for notification_children_count_bracketed (1769425473168347839) -->
+    <skip />
     <string name="safeMode" msgid="2788228061547930246">"Mode aman"</string>
     <string name="android_system_label" msgid="6577375335728551336">"Sistem Android"</string>
     <string name="user_owner_label" msgid="2804351898001038951">"Pribadi"</string>
diff --git a/core/res/res/values-is-rIS/strings.xml b/core/res/res/values-is-rIS/strings.xml
index 23db2d2..b2c98f0 100644
--- a/core/res/res/values-is-rIS/strings.xml
+++ b/core/res/res/values-is-rIS/strings.xml
@@ -222,6 +222,8 @@
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Raddaðstoð"</string>
     <string name="global_action_lockdown" msgid="8751542514724332873">"Læsa núna"</string>
     <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
+    <!-- no translation found for notification_children_count_bracketed (1769425473168347839) -->
+    <skip />
     <string name="safeMode" msgid="2788228061547930246">"Örugg stilling"</string>
     <string name="android_system_label" msgid="6577375335728551336">"Android kerfið"</string>
     <string name="user_owner_label" msgid="2804351898001038951">"Persónulegt"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index ff005ee..b36e2e5 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -222,6 +222,7 @@
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Voice Assist"</string>
     <string name="global_action_lockdown" msgid="8751542514724332873">"Blocca ora"</string>
     <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
+    <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g>)"</string>
     <string name="safeMode" msgid="2788228061547930246">"Modalità provvisoria"</string>
     <string name="android_system_label" msgid="6577375335728551336">"Sistema Android"</string>
     <string name="user_owner_label" msgid="2804351898001038951">"Personale"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 06cf587..cf4ca52 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -224,6 +224,8 @@
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Voice Assist"</string>
     <string name="global_action_lockdown" msgid="8751542514724332873">"נעל עכשיו"</string>
     <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
+    <!-- no translation found for notification_children_count_bracketed (1769425473168347839) -->
+    <skip />
     <string name="safeMode" msgid="2788228061547930246">"מצב בטוח"</string>
     <string name="android_system_label" msgid="6577375335728551336">"‏מערכת Android"</string>
     <string name="user_owner_label" msgid="2804351898001038951">"אישי"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 807dbd3..1ebf699 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -222,6 +222,7 @@
     <string name="global_action_voice_assist" msgid="7751191495200504480">"音声アシスト"</string>
     <string name="global_action_lockdown" msgid="8751542514724332873">"今すぐロック"</string>
     <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
+    <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g> 件)"</string>
     <string name="safeMode" msgid="2788228061547930246">"セーフモード"</string>
     <string name="android_system_label" msgid="6577375335728551336">"Androidシステム"</string>
     <string name="user_owner_label" msgid="2804351898001038951">"個人用"</string>
diff --git a/core/res/res/values-ka-rGE/strings.xml b/core/res/res/values-ka-rGE/strings.xml
index 9ce703f..604323a 100644
--- a/core/res/res/values-ka-rGE/strings.xml
+++ b/core/res/res/values-ka-rGE/strings.xml
@@ -222,6 +222,8 @@
     <string name="global_action_voice_assist" msgid="7751191495200504480">"ხმოვანი ასისტ."</string>
     <string name="global_action_lockdown" msgid="8751542514724332873">"ახლა ჩაკეტვა"</string>
     <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
+    <!-- no translation found for notification_children_count_bracketed (1769425473168347839) -->
+    <skip />
     <string name="safeMode" msgid="2788228061547930246">"უსაფრთხო რეჟიმი"</string>
     <string name="android_system_label" msgid="6577375335728551336">"Android-ის სისტემა"</string>
     <string name="user_owner_label" msgid="2804351898001038951">"პირადი"</string>
diff --git a/core/res/res/values-kk-rKZ/strings.xml b/core/res/res/values-kk-rKZ/strings.xml
index 7b3c756..323be5d 100644
--- a/core/res/res/values-kk-rKZ/strings.xml
+++ b/core/res/res/values-kk-rKZ/strings.xml
@@ -222,6 +222,8 @@
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Дауыс көмекшісі"</string>
     <string name="global_action_lockdown" msgid="8751542514724332873">"Қазір бекіту"</string>
     <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
+    <!-- no translation found for notification_children_count_bracketed (1769425473168347839) -->
+    <skip />
     <string name="safeMode" msgid="2788228061547930246">"Қауіпсіз режим"</string>
     <string name="android_system_label" msgid="6577375335728551336">"Android жүйесі"</string>
     <string name="user_owner_label" msgid="2804351898001038951">"Жеке"</string>
diff --git a/core/res/res/values-km-rKH/strings.xml b/core/res/res/values-km-rKH/strings.xml
index ad3b9bd..c00bc14 100644
--- a/core/res/res/values-km-rKH/strings.xml
+++ b/core/res/res/values-km-rKH/strings.xml
@@ -222,6 +222,8 @@
     <string name="global_action_voice_assist" msgid="7751191495200504480">"ជំនួយសម្លេង"</string>
     <string name="global_action_lockdown" msgid="8751542514724332873">"ចាក់សោ​ឥឡូវនេះ"</string>
     <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
+    <!-- no translation found for notification_children_count_bracketed (1769425473168347839) -->
+    <skip />
     <string name="safeMode" msgid="2788228061547930246">"របៀប​​​សុវត្ថិភាព"</string>
     <string name="android_system_label" msgid="6577375335728551336">"ប្រព័ន្ធ​​ Android"</string>
     <string name="user_owner_label" msgid="2804351898001038951">"ផ្ទាល់ខ្លួន"</string>
diff --git a/core/res/res/values-kn-rIN/strings.xml b/core/res/res/values-kn-rIN/strings.xml
index e828ce0..1aba0bb 100644
--- a/core/res/res/values-kn-rIN/strings.xml
+++ b/core/res/res/values-kn-rIN/strings.xml
@@ -222,6 +222,8 @@
     <string name="global_action_voice_assist" msgid="7751191495200504480">"ಧ್ವನಿ ಸಹಾಯಕ"</string>
     <string name="global_action_lockdown" msgid="8751542514724332873">"ಈಗ ಲಾಕ್ ಮಾಡಿ"</string>
     <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
+    <!-- no translation found for notification_children_count_bracketed (1769425473168347839) -->
+    <skip />
     <string name="safeMode" msgid="2788228061547930246">"ಸುರಕ್ಷಿತ ಮೋಡ್"</string>
     <string name="android_system_label" msgid="6577375335728551336">"Android ಸಿಸ್ಟಂ"</string>
     <string name="user_owner_label" msgid="2804351898001038951">"ವೈಯಕ್ತಿಕ"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 8e546ba..3384f09 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -222,6 +222,8 @@
     <string name="global_action_voice_assist" msgid="7751191495200504480">"음성 지원"</string>
     <string name="global_action_lockdown" msgid="8751542514724332873">"지금 잠그기"</string>
     <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
+    <!-- no translation found for notification_children_count_bracketed (1769425473168347839) -->
+    <skip />
     <string name="safeMode" msgid="2788228061547930246">"안전 모드"</string>
     <string name="android_system_label" msgid="6577375335728551336">"Android 시스템"</string>
     <string name="user_owner_label" msgid="2804351898001038951">"개인"</string>
@@ -229,7 +231,7 @@
     <string name="permgrouplab_contacts" msgid="3657758145679177612">"주소록"</string>
     <string name="permgroupdesc_contacts" msgid="6951499528303668046">"주소록 액세스"</string>
     <string name="permgrouplab_location" msgid="7275582855722310164">"위치"</string>
-    <string name="permgroupdesc_location" msgid="1346617465127855033">"이 기기의 위치에 액세스하기"</string>
+    <string name="permgroupdesc_location" msgid="1346617465127855033">"이 기기의 위치에 액세스"</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"캘린더"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"캘린더 액세스"</string>
     <string name="permgrouplab_sms" msgid="228308803364967808">"SMS"</string>
@@ -239,7 +241,7 @@
     <string name="permgrouplab_microphone" msgid="171539900250043464">"마이크"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"오디오 녹음"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"카메라"</string>
-    <string name="permgroupdesc_camera" msgid="3250611594678347720">"사진 찍기 및 동영상 녹화"</string>
+    <string name="permgroupdesc_camera" msgid="3250611594678347720">"사진 및 동영상 촬영"</string>
     <string name="permgrouplab_phone" msgid="5229115638567440675">"전화"</string>
     <string name="permgroupdesc_phone" msgid="6234224354060641055">"전화 걸기 및 관리"</string>
     <string name="permgrouplab_sensors" msgid="416037179223226722">"신체 센서"</string>
diff --git a/core/res/res/values-ky-rKG/strings.xml b/core/res/res/values-ky-rKG/strings.xml
index 55dd4ec..870c858 100644
--- a/core/res/res/values-ky-rKG/strings.xml
+++ b/core/res/res/values-ky-rKG/strings.xml
@@ -222,6 +222,8 @@
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Үн жардамчысы"</string>
     <string name="global_action_lockdown" msgid="8751542514724332873">"Азыр кулпулоо"</string>
     <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
+    <!-- no translation found for notification_children_count_bracketed (1769425473168347839) -->
+    <skip />
     <string name="safeMode" msgid="2788228061547930246">"Коопсуз режим"</string>
     <string name="android_system_label" msgid="6577375335728551336">"Android Тутуму"</string>
     <string name="user_owner_label" msgid="2804351898001038951">"Жеке"</string>
diff --git a/core/res/res/values-lo-rLA/strings.xml b/core/res/res/values-lo-rLA/strings.xml
index fa3be85..d5484d3 100644
--- a/core/res/res/values-lo-rLA/strings.xml
+++ b/core/res/res/values-lo-rLA/strings.xml
@@ -222,6 +222,8 @@
     <string name="global_action_voice_assist" msgid="7751191495200504480">"ຊ່ວຍ​ເຫຼືອ​ທາງ​ສຽງ"</string>
     <string name="global_action_lockdown" msgid="8751542514724332873">"ລັອກ​ດຽວ​ນີ້"</string>
     <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
+    <!-- no translation found for notification_children_count_bracketed (1769425473168347839) -->
+    <skip />
     <string name="safeMode" msgid="2788228061547930246">"Safe mode"</string>
     <string name="android_system_label" msgid="6577375335728551336">"ລະບົບ Android"</string>
     <string name="user_owner_label" msgid="2804351898001038951">"​ສ່ວນ​ໂຕ"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index cfcb639..6ee24cf 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -224,6 +224,8 @@
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Voice Assist"</string>
     <string name="global_action_lockdown" msgid="8751542514724332873">"Užrakinti dabar"</string>
     <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
+    <!-- no translation found for notification_children_count_bracketed (1769425473168347839) -->
+    <skip />
     <string name="safeMode" msgid="2788228061547930246">"Saugos režimas"</string>
     <string name="android_system_label" msgid="6577375335728551336">"„Android“ sistema"</string>
     <string name="user_owner_label" msgid="2804351898001038951">"Asmeninė"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 23be85a..7785965 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -223,6 +223,8 @@
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Balss palīgs"</string>
     <string name="global_action_lockdown" msgid="8751542514724332873">"Bloķēt tūlīt"</string>
     <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"Pārsniedz"</string>
+    <!-- no translation found for notification_children_count_bracketed (1769425473168347839) -->
+    <skip />
     <string name="safeMode" msgid="2788228061547930246">"Drošais režīms"</string>
     <string name="android_system_label" msgid="6577375335728551336">"Android sistēma"</string>
     <string name="user_owner_label" msgid="2804351898001038951">"Personisks"</string>
diff --git a/core/res/res/values-mk-rMK/strings.xml b/core/res/res/values-mk-rMK/strings.xml
index 99d5d46..eb90369 100644
--- a/core/res/res/values-mk-rMK/strings.xml
+++ b/core/res/res/values-mk-rMK/strings.xml
@@ -222,6 +222,7 @@
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Гласовна помош"</string>
     <string name="global_action_lockdown" msgid="8751542514724332873">"Заклучи сега"</string>
     <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
+    <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g>)"</string>
     <string name="safeMode" msgid="2788228061547930246">"Безбеден режим"</string>
     <string name="android_system_label" msgid="6577375335728551336">"Систем Android"</string>
     <string name="user_owner_label" msgid="2804351898001038951">"Лични"</string>
diff --git a/core/res/res/values-ml-rIN/strings.xml b/core/res/res/values-ml-rIN/strings.xml
index f114cb9..a83928e 100644
--- a/core/res/res/values-ml-rIN/strings.xml
+++ b/core/res/res/values-ml-rIN/strings.xml
@@ -222,6 +222,8 @@
     <string name="global_action_voice_assist" msgid="7751191495200504480">"വോയ്‌സ് സഹായം"</string>
     <string name="global_action_lockdown" msgid="8751542514724332873">"ഇപ്പോൾ ലോക്കുചെയ്യുക"</string>
     <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
+    <!-- no translation found for notification_children_count_bracketed (1769425473168347839) -->
+    <skip />
     <string name="safeMode" msgid="2788228061547930246">"സുരക്ഷിത മോഡ്"</string>
     <string name="android_system_label" msgid="6577375335728551336">"Android സിസ്റ്റം"</string>
     <string name="user_owner_label" msgid="2804351898001038951">"വ്യക്തിഗതം"</string>
diff --git a/core/res/res/values-mn-rMN/strings.xml b/core/res/res/values-mn-rMN/strings.xml
index ac226a4..a9fb8593 100644
--- a/core/res/res/values-mn-rMN/strings.xml
+++ b/core/res/res/values-mn-rMN/strings.xml
@@ -222,6 +222,8 @@
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Дуут туслах"</string>
     <string name="global_action_lockdown" msgid="8751542514724332873">"Одоо түгжих"</string>
     <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
+    <!-- no translation found for notification_children_count_bracketed (1769425473168347839) -->
+    <skip />
     <string name="safeMode" msgid="2788228061547930246">"Аюулгүй горим"</string>
     <string name="android_system_label" msgid="6577375335728551336">"Андройд систем"</string>
     <string name="user_owner_label" msgid="2804351898001038951">"Хувийн"</string>
diff --git a/core/res/res/values-mr-rIN/strings.xml b/core/res/res/values-mr-rIN/strings.xml
index 6ae6e82..a8896c3 100644
--- a/core/res/res/values-mr-rIN/strings.xml
+++ b/core/res/res/values-mr-rIN/strings.xml
@@ -222,6 +222,8 @@
     <string name="global_action_voice_assist" msgid="7751191495200504480">"व्हॉइस सहाय्य"</string>
     <string name="global_action_lockdown" msgid="8751542514724332873">"आता लॉक करा"</string>
     <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
+    <!-- no translation found for notification_children_count_bracketed (1769425473168347839) -->
+    <skip />
     <string name="safeMode" msgid="2788228061547930246">"सुरक्षित मोड"</string>
     <string name="android_system_label" msgid="6577375335728551336">"Android सिस्‍टम"</string>
     <string name="user_owner_label" msgid="2804351898001038951">"वैयक्तिक"</string>
diff --git a/core/res/res/values-ms-rMY/strings.xml b/core/res/res/values-ms-rMY/strings.xml
index d9175c2..cababdd 100644
--- a/core/res/res/values-ms-rMY/strings.xml
+++ b/core/res/res/values-ms-rMY/strings.xml
@@ -222,6 +222,8 @@
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Bantuan Suara"</string>
     <string name="global_action_lockdown" msgid="8751542514724332873">"Kunci sekarang"</string>
     <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
+    <!-- no translation found for notification_children_count_bracketed (1769425473168347839) -->
+    <skip />
     <string name="safeMode" msgid="2788228061547930246">"Mod selamat"</string>
     <string name="android_system_label" msgid="6577375335728551336">"Sistem Android"</string>
     <string name="user_owner_label" msgid="2804351898001038951">"Peribadi"</string>
diff --git a/core/res/res/values-my-rMM/strings.xml b/core/res/res/values-my-rMM/strings.xml
index 4a0a227..8e61f41 100644
--- a/core/res/res/values-my-rMM/strings.xml
+++ b/core/res/res/values-my-rMM/strings.xml
@@ -222,6 +222,8 @@
     <string name="global_action_voice_assist" msgid="7751191495200504480">"အသံ အကူအညီ"</string>
     <string name="global_action_lockdown" msgid="8751542514724332873">"ယခု သော့ပိတ်ရန်"</string>
     <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"၉၉၉+"</string>
+    <!-- no translation found for notification_children_count_bracketed (1769425473168347839) -->
+    <skip />
     <string name="safeMode" msgid="2788228061547930246">"အန္တရာယ်ကင်းမှု စနစ်(Safe mode)"</string>
     <string name="android_system_label" msgid="6577375335728551336">"Android စနစ်"</string>
     <string name="user_owner_label" msgid="2804351898001038951">"ကိုယ်ရေး"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index f504175..f29a903 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -222,6 +222,8 @@
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Talehjelp"</string>
     <string name="global_action_lockdown" msgid="8751542514724332873">"Lås nå"</string>
     <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
+    <!-- no translation found for notification_children_count_bracketed (1769425473168347839) -->
+    <skip />
     <string name="safeMode" msgid="2788228061547930246">"Sikkermodus"</string>
     <string name="android_system_label" msgid="6577375335728551336">"Android-system"</string>
     <string name="user_owner_label" msgid="2804351898001038951">"Personlig"</string>
@@ -235,7 +237,7 @@
     <string name="permgrouplab_sms" msgid="228308803364967808">"SMS"</string>
     <string name="permgroupdesc_sms" msgid="4656988620100940350">"sende og lese SMS-meldinger"</string>
     <string name="permgrouplab_storage" msgid="1971118770546336966">"Lagring"</string>
-    <string name="permgroupdesc_storage" msgid="637758554581589203">"åpne bilder, media og filer på enheten din"</string>
+    <string name="permgroupdesc_storage" msgid="637758554581589203">"åpne bilder, medier og filer på enheten din"</string>
     <string name="permgrouplab_microphone" msgid="171539900250043464">"Mikrofon"</string>
     <string name="permgroupdesc_microphone" msgid="4988812113943554584">"spill inn lyd"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Kamera"</string>
diff --git a/core/res/res/values-ne-rNP/strings.xml b/core/res/res/values-ne-rNP/strings.xml
index 7867c59..a33289f 100644
--- a/core/res/res/values-ne-rNP/strings.xml
+++ b/core/res/res/values-ne-rNP/strings.xml
@@ -222,6 +222,8 @@
     <string name="global_action_voice_assist" msgid="7751191495200504480">"आवाज सहायता"</string>
     <string name="global_action_lockdown" msgid="8751542514724332873">"अब बन्द गर्नुहोस्"</string>
     <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"९९९+"</string>
+    <!-- no translation found for notification_children_count_bracketed (1769425473168347839) -->
+    <skip />
     <string name="safeMode" msgid="2788228061547930246">"सुरक्षित मोड"</string>
     <string name="android_system_label" msgid="6577375335728551336">"एन्ड्रोइड प्रणाली"</string>
     <string name="user_owner_label" msgid="2804351898001038951">"व्यक्तिगत"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index d04e10b..53b45b0 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -222,6 +222,7 @@
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Spraakassistent"</string>
     <string name="global_action_lockdown" msgid="8751542514724332873">"Nu vergrendelen"</string>
     <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999 +"</string>
+    <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g>)"</string>
     <string name="safeMode" msgid="2788228061547930246">"Veilige modus"</string>
     <string name="android_system_label" msgid="6577375335728551336">"Android-systeem"</string>
     <string name="user_owner_label" msgid="2804351898001038951">"Persoonlijk"</string>
@@ -243,7 +244,7 @@
     <string name="permgrouplab_phone" msgid="5229115638567440675">"Telefoon"</string>
     <string name="permgroupdesc_phone" msgid="6234224354060641055">"bellen en telefoontjes beheren"</string>
     <string name="permgrouplab_sensors" msgid="416037179223226722">"Lichaamssensoren"</string>
-    <string name="permgroupdesc_sensors" msgid="7147968539346634043">"toegang tot sensorgegevens over je vitale functies"</string>
+    <string name="permgroupdesc_sensors" msgid="7147968539346634043">"toegang krijgen tot sensorgegevens over je vitale functies"</string>
     <string name="capability_title_canRetrieveWindowContent" msgid="3901717936930170320">"Inhoud van vensters ophalen"</string>
     <string name="capability_desc_canRetrieveWindowContent" msgid="3772225008605310672">"De inhoud inspecteren van een venster waarmee je interactie hebt."</string>
     <string name="capability_title_canRequestTouchExploration" msgid="3108723364676667320">"\'Verkennen via aanraking\' inschakelen"</string>
diff --git a/core/res/res/values-pa-rIN/strings.xml b/core/res/res/values-pa-rIN/strings.xml
index c3df4ac..d450bf9 100644
--- a/core/res/res/values-pa-rIN/strings.xml
+++ b/core/res/res/values-pa-rIN/strings.xml
@@ -222,6 +222,8 @@
     <string name="global_action_voice_assist" msgid="7751191495200504480">"ਵੌਇਸ ਅਸਿਸਟ"</string>
     <string name="global_action_lockdown" msgid="8751542514724332873">"ਹੁਣ ਲੌਕ ਕਰੋ"</string>
     <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
+    <!-- no translation found for notification_children_count_bracketed (1769425473168347839) -->
+    <skip />
     <string name="safeMode" msgid="2788228061547930246">"ਸੁਰੱਖਿਅਤ ਮੋਡ"</string>
     <string name="android_system_label" msgid="6577375335728551336">"Android System"</string>
     <string name="user_owner_label" msgid="2804351898001038951">"ਨਿੱਜੀ"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index ad826262..b7f984d 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -224,6 +224,8 @@
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Asystent głosowy"</string>
     <string name="global_action_lockdown" msgid="8751542514724332873">"Zablokuj teraz"</string>
     <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"&gt;999"</string>
+    <!-- no translation found for notification_children_count_bracketed (1769425473168347839) -->
+    <skip />
     <string name="safeMode" msgid="2788228061547930246">"Tryb awaryjny"</string>
     <string name="android_system_label" msgid="6577375335728551336">"System Android"</string>
     <string name="user_owner_label" msgid="2804351898001038951">"Osobiste"</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index c338b3b..4464259 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -222,6 +222,8 @@
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Ajuda de voz"</string>
     <string name="global_action_lockdown" msgid="8751542514724332873">"Bloquear agora"</string>
     <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"&gt;999"</string>
+    <!-- no translation found for notification_children_count_bracketed (1769425473168347839) -->
+    <skip />
     <string name="safeMode" msgid="2788228061547930246">"Modo de segurança"</string>
     <string name="android_system_label" msgid="6577375335728551336">"Sistema Android"</string>
     <string name="user_owner_label" msgid="2804351898001038951">"Pessoal"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 6b0581f..af38435 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -222,6 +222,8 @@
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Assist. de voz"</string>
     <string name="global_action_lockdown" msgid="8751542514724332873">"Bloquear agora"</string>
     <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
+    <!-- no translation found for notification_children_count_bracketed (1769425473168347839) -->
+    <skip />
     <string name="safeMode" msgid="2788228061547930246">"Modo seguro"</string>
     <string name="android_system_label" msgid="6577375335728551336">"Sistema Android"</string>
     <string name="user_owner_label" msgid="2804351898001038951">"Pessoal"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index c338b3b..4464259 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -222,6 +222,8 @@
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Ajuda de voz"</string>
     <string name="global_action_lockdown" msgid="8751542514724332873">"Bloquear agora"</string>
     <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"&gt;999"</string>
+    <!-- no translation found for notification_children_count_bracketed (1769425473168347839) -->
+    <skip />
     <string name="safeMode" msgid="2788228061547930246">"Modo de segurança"</string>
     <string name="android_system_label" msgid="6577375335728551336">"Sistema Android"</string>
     <string name="user_owner_label" msgid="2804351898001038951">"Pessoal"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 9e676fd..7ac92c3 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -223,6 +223,8 @@
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Asistent vocal"</string>
     <string name="global_action_lockdown" msgid="8751542514724332873">"Blocați acum"</string>
     <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"˃999"</string>
+    <!-- no translation found for notification_children_count_bracketed (1769425473168347839) -->
+    <skip />
     <string name="safeMode" msgid="2788228061547930246">"Mod sigur"</string>
     <string name="android_system_label" msgid="6577375335728551336">"Sistemul Android"</string>
     <string name="user_owner_label" msgid="2804351898001038951">"Personal"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index ca1a986..b7ba99f 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -224,6 +224,8 @@
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Аудиоподсказки"</string>
     <string name="global_action_lockdown" msgid="8751542514724332873">"Заблокировать"</string>
     <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"&gt;999"</string>
+    <!-- no translation found for notification_children_count_bracketed (1769425473168347839) -->
+    <skip />
     <string name="safeMode" msgid="2788228061547930246">"Безопасный режим"</string>
     <string name="android_system_label" msgid="6577375335728551336">"Система Android"</string>
     <string name="user_owner_label" msgid="2804351898001038951">"Личные данные"</string>
diff --git a/core/res/res/values-si-rLK/strings.xml b/core/res/res/values-si-rLK/strings.xml
index 66061d1..4e265177 100644
--- a/core/res/res/values-si-rLK/strings.xml
+++ b/core/res/res/values-si-rLK/strings.xml
@@ -222,6 +222,8 @@
     <string name="global_action_voice_assist" msgid="7751191495200504480">"හඬ සහායක"</string>
     <string name="global_action_lockdown" msgid="8751542514724332873">"දැන් අගුළු දමන්න"</string>
     <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
+    <!-- no translation found for notification_children_count_bracketed (1769425473168347839) -->
+    <skip />
     <string name="safeMode" msgid="2788228061547930246">"ආරක්‍ෂිත ආකාරය"</string>
     <string name="android_system_label" msgid="6577375335728551336">"Android පද්ධතිය"</string>
     <string name="user_owner_label" msgid="2804351898001038951">"පෞද්ගලික"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 72e8db2..ba5465e 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -224,6 +224,8 @@
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Hlasový asistent"</string>
     <string name="global_action_lockdown" msgid="8751542514724332873">"Uzamknúť"</string>
     <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
+    <!-- no translation found for notification_children_count_bracketed (1769425473168347839) -->
+    <skip />
     <string name="safeMode" msgid="2788228061547930246">"Núdzový režim"</string>
     <string name="android_system_label" msgid="6577375335728551336">"Systém Android"</string>
     <string name="user_owner_label" msgid="2804351898001038951">"Osobné"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 9f1c669..04b7fc7 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -224,6 +224,8 @@
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Glas. pomočnik"</string>
     <string name="global_action_lockdown" msgid="8751542514724332873">"Zakleni zdaj"</string>
     <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999 +"</string>
+    <!-- no translation found for notification_children_count_bracketed (1769425473168347839) -->
+    <skip />
     <string name="safeMode" msgid="2788228061547930246">"Varni način"</string>
     <string name="android_system_label" msgid="6577375335728551336">"Sistem Android"</string>
     <string name="user_owner_label" msgid="2804351898001038951">"Osebno"</string>
diff --git a/core/res/res/values-sq-rAL/strings.xml b/core/res/res/values-sq-rAL/strings.xml
index d67a5fe..b8ca565 100644
--- a/core/res/res/values-sq-rAL/strings.xml
+++ b/core/res/res/values-sq-rAL/strings.xml
@@ -222,6 +222,7 @@
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Ndihma zanore"</string>
     <string name="global_action_lockdown" msgid="8751542514724332873">"Kyç tani"</string>
     <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
+    <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g>)"</string>
     <string name="safeMode" msgid="2788228061547930246">"Modaliteti i sigurisë"</string>
     <string name="android_system_label" msgid="6577375335728551336">"Sistemi \"android\""</string>
     <string name="user_owner_label" msgid="2804351898001038951">"Personal"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index ddb9ba7..4118c63 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -223,6 +223,8 @@
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Гласовна помоћ"</string>
     <string name="global_action_lockdown" msgid="8751542514724332873">"Закључај одмах"</string>
     <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
+    <!-- no translation found for notification_children_count_bracketed (1769425473168347839) -->
+    <skip />
     <string name="safeMode" msgid="2788228061547930246">"Безбедни режим"</string>
     <string name="android_system_label" msgid="6577375335728551336">"Android систем"</string>
     <string name="user_owner_label" msgid="2804351898001038951">"Лично"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index f5172fe..40141de 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -222,6 +222,8 @@
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Voice Assist"</string>
     <string name="global_action_lockdown" msgid="8751542514724332873">"Lås nu"</string>
     <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
+    <!-- no translation found for notification_children_count_bracketed (1769425473168347839) -->
+    <skip />
     <string name="safeMode" msgid="2788228061547930246">"Säkert läge"</string>
     <string name="android_system_label" msgid="6577375335728551336">"Android-system"</string>
     <string name="user_owner_label" msgid="2804351898001038951">"Personligt"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 3de8eeb..196d5dc 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -224,6 +224,8 @@
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Usaidizi wa Sauti"</string>
     <string name="global_action_lockdown" msgid="8751542514724332873">"Funga sasa"</string>
     <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
+    <!-- no translation found for notification_children_count_bracketed (1769425473168347839) -->
+    <skip />
     <string name="safeMode" msgid="2788228061547930246">"Mtindo salama"</string>
     <string name="android_system_label" msgid="6577375335728551336">"Mfumo wa Android"</string>
     <string name="user_owner_label" msgid="2804351898001038951">"Binafsi"</string>
diff --git a/core/res/res/values-ta-rIN/strings.xml b/core/res/res/values-ta-rIN/strings.xml
index 30b326a..1113776 100644
--- a/core/res/res/values-ta-rIN/strings.xml
+++ b/core/res/res/values-ta-rIN/strings.xml
@@ -222,6 +222,8 @@
     <string name="global_action_voice_assist" msgid="7751191495200504480">"குரல் உதவி"</string>
     <string name="global_action_lockdown" msgid="8751542514724332873">"இப்போது பூட்டு"</string>
     <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
+    <!-- no translation found for notification_children_count_bracketed (1769425473168347839) -->
+    <skip />
     <string name="safeMode" msgid="2788228061547930246">"பாதுகாப்பு பயன்முறை"</string>
     <string name="android_system_label" msgid="6577375335728551336">"Android அமைப்பு"</string>
     <string name="user_owner_label" msgid="2804351898001038951">"தனிப்பட்ட"</string>
diff --git a/core/res/res/values-te-rIN/strings.xml b/core/res/res/values-te-rIN/strings.xml
index 3659080..c563e7c 100644
--- a/core/res/res/values-te-rIN/strings.xml
+++ b/core/res/res/values-te-rIN/strings.xml
@@ -222,6 +222,8 @@
     <string name="global_action_voice_assist" msgid="7751191495200504480">"వాయిస్ సహాయకం"</string>
     <string name="global_action_lockdown" msgid="8751542514724332873">"ఇప్పుడు లాక్ చేయండి"</string>
     <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
+    <!-- no translation found for notification_children_count_bracketed (1769425473168347839) -->
+    <skip />
     <string name="safeMode" msgid="2788228061547930246">"సురక్షిత మోడ్"</string>
     <string name="android_system_label" msgid="6577375335728551336">"Android సిస్టమ్"</string>
     <string name="user_owner_label" msgid="2804351898001038951">"వ్యక్తిగతం"</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 81bc75f..0080f51 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -222,6 +222,8 @@
     <string name="global_action_voice_assist" msgid="7751191495200504480">"ตัวช่วยเสียง"</string>
     <string name="global_action_lockdown" msgid="8751542514724332873">"ล็อกเลย"</string>
     <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
+    <!-- no translation found for notification_children_count_bracketed (1769425473168347839) -->
+    <skip />
     <string name="safeMode" msgid="2788228061547930246">"โหมดปลอดภัย"</string>
     <string name="android_system_label" msgid="6577375335728551336">"ระบบ Android"</string>
     <string name="user_owner_label" msgid="2804351898001038951">"ส่วนตัว"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 29efae69..4f907c1 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -222,6 +222,8 @@
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Voice Assist"</string>
     <string name="global_action_lockdown" msgid="8751542514724332873">"I-lock ngayon"</string>
     <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
+    <!-- no translation found for notification_children_count_bracketed (1769425473168347839) -->
+    <skip />
     <string name="safeMode" msgid="2788228061547930246">"Safe mode"</string>
     <string name="android_system_label" msgid="6577375335728551336">"Android System"</string>
     <string name="user_owner_label" msgid="2804351898001038951">"Personal"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index dac5259..06f8c75 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -222,6 +222,8 @@
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Sesli Yardım"</string>
     <string name="global_action_lockdown" msgid="8751542514724332873">"Şimdi kilitle"</string>
     <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
+    <!-- no translation found for notification_children_count_bracketed (1769425473168347839) -->
+    <skip />
     <string name="safeMode" msgid="2788228061547930246">"Güvenli mod"</string>
     <string name="android_system_label" msgid="6577375335728551336">"Android Sistemi"</string>
     <string name="user_owner_label" msgid="2804351898001038951">"Kişisel"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 7522e4e..721f24d 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -224,6 +224,7 @@
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Голос. підказки"</string>
     <string name="global_action_lockdown" msgid="8751542514724332873">"Блокувати зараз"</string>
     <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
+    <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g>)"</string>
     <string name="safeMode" msgid="2788228061547930246">"Безп. режим"</string>
     <string name="android_system_label" msgid="6577375335728551336">"Система Android"</string>
     <string name="user_owner_label" msgid="2804351898001038951">"Особисті дані"</string>
diff --git a/core/res/res/values-ur-rPK/strings.xml b/core/res/res/values-ur-rPK/strings.xml
index 388b790..e31fa57 100644
--- a/core/res/res/values-ur-rPK/strings.xml
+++ b/core/res/res/values-ur-rPK/strings.xml
@@ -222,6 +222,8 @@
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Voice Assist"</string>
     <string name="global_action_lockdown" msgid="8751542514724332873">"ابھی مقفل کریں"</string>
     <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"‎999+‎"</string>
+    <!-- no translation found for notification_children_count_bracketed (1769425473168347839) -->
+    <skip />
     <string name="safeMode" msgid="2788228061547930246">"حفاظتی وضع"</string>
     <string name="android_system_label" msgid="6577375335728551336">"‏Android سسٹم"</string>
     <string name="user_owner_label" msgid="2804351898001038951">"ذاتی"</string>
diff --git a/core/res/res/values-uz-rUZ/strings.xml b/core/res/res/values-uz-rUZ/strings.xml
index bafedfc..0489638 100644
--- a/core/res/res/values-uz-rUZ/strings.xml
+++ b/core/res/res/values-uz-rUZ/strings.xml
@@ -222,6 +222,8 @@
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Ovozli yordam"</string>
     <string name="global_action_lockdown" msgid="8751542514724332873">"Qulflash"</string>
     <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
+    <!-- no translation found for notification_children_count_bracketed (1769425473168347839) -->
+    <skip />
     <string name="safeMode" msgid="2788228061547930246">"Xavfsiz usul"</string>
     <string name="android_system_label" msgid="6577375335728551336">"Android tizimi"</string>
     <string name="user_owner_label" msgid="2804351898001038951">"Shaxsiy"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 9165f4d..ccd1345 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -222,6 +222,8 @@
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Trợ lý thoại"</string>
     <string name="global_action_lockdown" msgid="8751542514724332873">"Khóa ngay"</string>
     <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
+    <!-- no translation found for notification_children_count_bracketed (1769425473168347839) -->
+    <skip />
     <string name="safeMode" msgid="2788228061547930246">"Chế độ an toàn"</string>
     <string name="android_system_label" msgid="6577375335728551336">"Hệ thống Android"</string>
     <string name="user_owner_label" msgid="2804351898001038951">"Cá nhân"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 36a1a7f..1afede0 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -222,6 +222,8 @@
     <string name="global_action_voice_assist" msgid="7751191495200504480">"语音助理"</string>
     <string name="global_action_lockdown" msgid="8751542514724332873">"立即锁定"</string>
     <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
+    <!-- no translation found for notification_children_count_bracketed (1769425473168347839) -->
+    <skip />
     <string name="safeMode" msgid="2788228061547930246">"安全模式"</string>
     <string name="android_system_label" msgid="6577375335728551336">"Android 系统"</string>
     <string name="user_owner_label" msgid="2804351898001038951">"个人"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index efab307..84c85bf 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -222,6 +222,8 @@
     <string name="global_action_voice_assist" msgid="7751191495200504480">"語音助手"</string>
     <string name="global_action_lockdown" msgid="8751542514724332873">"立即鎖定"</string>
     <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
+    <!-- no translation found for notification_children_count_bracketed (1769425473168347839) -->
+    <skip />
     <string name="safeMode" msgid="2788228061547930246">"安全模式"</string>
     <string name="android_system_label" msgid="6577375335728551336">"Android 系統"</string>
     <string name="user_owner_label" msgid="2804351898001038951">"個人"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index e938fb8..b45fe75 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -222,6 +222,8 @@
     <string name="global_action_voice_assist" msgid="7751191495200504480">"語音小幫手"</string>
     <string name="global_action_lockdown" msgid="8751542514724332873">"立即鎖定"</string>
     <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"超過 999"</string>
+    <!-- no translation found for notification_children_count_bracketed (1769425473168347839) -->
+    <skip />
     <string name="safeMode" msgid="2788228061547930246">"安全模式"</string>
     <string name="android_system_label" msgid="6577375335728551336">"Android 系統"</string>
     <string name="user_owner_label" msgid="2804351898001038951">"個人"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index bd566cd..c2159d7 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -222,6 +222,7 @@
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Isisekeli sezwi"</string>
     <string name="global_action_lockdown" msgid="8751542514724332873">"Khiya manje"</string>
     <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
+    <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g>)"</string>
     <string name="safeMode" msgid="2788228061547930246">"Imodi ephephile"</string>
     <string name="android_system_label" msgid="6577375335728551336">"Uhlelo lwe-Android"</string>
     <string name="user_owner_label" msgid="2804351898001038951">"Okomuntu siqu"</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 9dbdaaa..3860f68 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2268,6 +2268,7 @@
   <java-symbol type="layout" name="floating_popup_menu_image_button" />
   <java-symbol type="layout" name="floating_popup_overflow_list_item" />
   <java-symbol type="layout" name="floating_popup_overflow_image_list_item" />
+  <java-symbol type="layout" name="floating_popup_overflow_button" />
   <java-symbol type="dimen" name="floating_toolbar_height" />
   <java-symbol type="dimen" name="floating_toolbar_menu_button_side_padding" />
   <java-symbol type="dimen" name="floating_toolbar_overflow_side_padding" />
@@ -2279,6 +2280,10 @@
   <java-symbol type="dimen" name="floating_toolbar_horizontal_margin" />
   <java-symbol type="dimen" name="floating_toolbar_vertical_margin" />
   <java-symbol type="dimen" name="content_rect_bottom_clip_allowance" />
+  <java-symbol type="drawable" name="ft_avd_tooverflow" />
+  <java-symbol type="drawable" name="ft_avd_toarrow" />
+  <java-symbol type="drawable" name="ft_avd_toarrow_animation" />
+  <java-symbol type="drawable" name="ft_avd_tooverflow_animation" />
 
   <java-symbol type="string" name="date_picker_prev_month_button" />
   <java-symbol type="string" name="date_picker_next_month_button" />
diff --git a/core/tests/coretests/apks/install_complete_package_info/Android.mk b/core/tests/coretests/apks/install_complete_package_info/Android.mk
index 1edccb4..19bf356 100644
--- a/core/tests/coretests/apks/install_complete_package_info/Android.mk
+++ b/core/tests/coretests/apks/install_complete_package_info/Android.mk
@@ -5,7 +5,9 @@
 
 LOCAL_SRC_FILES := $(call all-subdir-java-files)
 
-LOCAL_PACKAGE_NAME := FrameworkCoreTests_install_complete_package_info
+LOCAL_PACKAGE_NAME := install_complete_package_info
+#LOCAL_MANIFEST_FILE := api_test/AndroidManifest.xml
 
-include $(BUILD_PACKAGE)
+include $(FrameworkCoreTests_BUILD_PACKAGE)
+#include $(BUILD_PACKAGE)
 
diff --git a/core/tests/coretests/apks/keyset/api_test/AndroidManifest.xml b/core/tests/coretests/apks/keyset/api_test/AndroidManifest.xml
index 4c7e968..2897bd5 100644
--- a/core/tests/coretests/apks/keyset/api_test/AndroidManifest.xml
+++ b/core/tests/coretests/apks/keyset/api_test/AndroidManifest.xml
@@ -17,4 +17,11 @@
         package="com.android.frameworks.coretests.keysets_api">
     <application android:hasCode="false">
     </application>
+    <key-sets>
+        <key-set android:name="A" >
+          <public-key android:name="keyA"
+                      android:value="MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJoN1Nsgqf0V4C/bbN8wo8O2X/S5D76+5Mb9mlIsHkUTUTbHCNk+LxHIUYLm89YbP9zImrV0bUHLUAZUyoMUCiMCAwEAAQ=="/>
+        </key-set>
+    </key-sets>
+
 </manifest>
diff --git a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
index 9498f4c..b5f0617 100644
--- a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
+++ b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
@@ -73,7 +73,6 @@
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
-@Suppress  // Failing.
 public class PackageManagerTests extends AndroidTestCase {
     private static final boolean localLOGV = true;
 
@@ -434,14 +433,6 @@
                             SECURE_CONTAINERS_PREFIX, publicSrcPath);
                     assertStartsWith("The native library path should point to the ASEC",
                             SECURE_CONTAINERS_PREFIX, info.nativeLibraryDir);
-                    try {
-                        String compatLib = new File(info.dataDir + "/lib").getCanonicalPath();
-                        assertEquals("The compatibility lib directory should be a symbolic link to "
-                                + info.nativeLibraryDir,
-                                info.nativeLibraryDir, compatLib);
-                    } catch (IOException e) {
-                        fail("compat check: Can't read " + info.dataDir + "/lib");
-                    }
                 } else {
                     assertFalse(
                             (info.privateFlags & ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK) != 0);
@@ -1014,7 +1005,8 @@
 
     private static void assertUninstalled(ApplicationInfo info) throws Exception {
         File nativeLibraryFile = new File(info.nativeLibraryDir);
-        assertFalse("Native library directory should be erased", nativeLibraryFile.exists());
+        assertFalse("Native library directory " + info.nativeLibraryDir
+                + " should be erased", nativeLibraryFile.exists());
     }
 
     public void deleteFromRawResource(int iFlags, int dFlags) throws Exception {
@@ -1650,15 +1642,10 @@
                             (info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0);
                     assertStartsWith("Native library dir should point to ASEC",
                             SECURE_CONTAINERS_PREFIX, info.nativeLibraryDir);
-                    final File nativeLibSymLink = new File(info.dataDir, "lib");
-                    assertStartsWith("The data directory should have a 'lib' symlink that points to the ASEC container",
-                            SECURE_CONTAINERS_PREFIX, nativeLibSymLink.getCanonicalPath());
                 }
             }
         } catch (NameNotFoundException e) {
             failStr("Pkg hasnt been installed correctly");
-        } catch (Exception e) {
-            failStr("Failed with exception : " + e);
         } finally {
             if (ip != null) {
                 cleanUpInstall(ip);
@@ -1689,6 +1676,7 @@
         sampleMoveFromRawResource(installFlags, moveFlags, fail, result);
     }
 
+    @Suppress
     @LargeTest
     public void testMoveAppInternalToInternal() throws Exception {
         int installFlags = PackageManager.INSTALL_INTERNAL;
@@ -2157,6 +2145,7 @@
                 -1);
     }
 
+    @Suppress
     @LargeTest
     public void testFlagFExistingI() throws Exception {
         int iFlags = PackageManager.INSTALL_INTERNAL;
@@ -3272,14 +3261,15 @@
             assertTrue(false); // should have thrown
         } catch (IllegalArgumentException e) {
         }
-        installFromRawResource("keysetApi.apk", R.raw.keyset_splat_api,
+        final InstallParams ip = installFromRawResource("keysetApi.apk", R.raw.keyset_splat_api,
                 0, false, false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
         try {
             ks = pm.getSigningKeySet(otherPkgName);
             assertTrue(false); // should have thrown
         } catch (SecurityException e) {
+        } finally {
+            cleanUpInstall(ip);
         }
-        cleanUpInstall(otherPkgName);
         ks = pm.getSigningKeySet(mContext.getPackageName());
         assertNotNull(ks);
     }
@@ -3318,16 +3308,20 @@
             assertTrue(false); // should have thrown
         } catch(IllegalArgumentException e) {
         }
-        installFromRawResource("keysetApi.apk", R.raw.keyset_splat_api,
+
+        // make sure we can get a KeySet from our pkg
+        ks = pm.getKeySetByAlias(mPkgName, "A");
+        assertNotNull(ks);
+
+        // and another
+        final InstallParams ip = installFromRawResource("keysetApi.apk", R.raw.keyset_splat_api,
                 0, false, false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
         try {
             ks = pm.getKeySetByAlias(otherPkgName, "A");
-            assertTrue(false); // should have thrown
-        } catch (SecurityException e) {
+            assertNotNull(ks);
+        } finally {
+            cleanUpInstall(ip);
         }
-        cleanUpInstall(otherPkgName);
-        ks = pm.getKeySetByAlias(mPkgName, "A");
-        assertNotNull(ks);
     }
 
     public void testIsSignedBy() throws Exception {
@@ -3360,17 +3354,23 @@
         assertFalse(pm.isSignedBy(mPkgName, new KeySet(new Binder())));
         assertTrue(pm.isSignedBy(mPkgName, mSigningKS));
 
-        installFromRawResource("keysetApi.apk", R.raw.keyset_splat_api,
+        final InstallParams ip1 = installFromRawResource("keysetApi.apk", R.raw.keyset_splat_api,
                 0, false, false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
-        assertFalse(pm.isSignedBy(otherPkgName, mDefinedKS));
-        assertTrue(pm.isSignedBy(otherPkgName, mSigningKS));
-        cleanUpInstall(otherPkgName);
+        try {
+            assertFalse(pm.isSignedBy(otherPkgName, mDefinedKS));
+            assertTrue(pm.isSignedBy(otherPkgName, mSigningKS));
+        } finally {
+            cleanUpInstall(ip1);
+        }
 
-        installFromRawResource("keysetApi.apk", R.raw.keyset_splata_api,
+        final InstallParams ip2 = installFromRawResource("keysetApi.apk", R.raw.keyset_splata_api,
                 0, false, false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
-        assertTrue(pm.isSignedBy(otherPkgName, mDefinedKS));
-        assertTrue(pm.isSignedBy(otherPkgName, mSigningKS));
-        cleanUpInstall(otherPkgName);
+        try {
+            assertTrue(pm.isSignedBy(otherPkgName, mDefinedKS));
+            assertTrue(pm.isSignedBy(otherPkgName, mSigningKS));
+        } finally {
+            cleanUpInstall(ip2);
+        }
     }
 
     public void testIsSignedByExactly() throws Exception {
@@ -3402,17 +3402,23 @@
         assertFalse(pm.isSignedByExactly(mPkgName, new KeySet(new Binder())));
         assertTrue(pm.isSignedByExactly(mPkgName, mSigningKS));
 
-        installFromRawResource("keysetApi.apk", R.raw.keyset_splat_api,
+        final InstallParams ip1 = installFromRawResource("keysetApi.apk", R.raw.keyset_splat_api,
                 0, false, false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
-        assertFalse(pm.isSignedByExactly(otherPkgName, mDefinedKS));
-        assertTrue(pm.isSignedByExactly(otherPkgName, mSigningKS));
-        cleanUpInstall(otherPkgName);
+        try {
+            assertFalse(pm.isSignedByExactly(otherPkgName, mDefinedKS));
+            assertTrue(pm.isSignedByExactly(otherPkgName, mSigningKS));
+        } finally {
+            cleanUpInstall(ip1);
+        }
 
-        installFromRawResource("keysetApi.apk", R.raw.keyset_splata_api,
+        final InstallParams ip2 = installFromRawResource("keysetApi.apk", R.raw.keyset_splata_api,
                 0, false, false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
-        assertFalse(pm.isSignedByExactly(otherPkgName, mDefinedKS));
-        assertFalse(pm.isSignedByExactly(otherPkgName, mSigningKS));
-        cleanUpInstall(otherPkgName);
+        try {
+            assertFalse(pm.isSignedByExactly(otherPkgName, mDefinedKS));
+            assertFalse(pm.isSignedByExactly(otherPkgName, mSigningKS));
+        } finally {
+            cleanUpInstall(ip2);
+        }
     }
 
 
@@ -3465,11 +3471,10 @@
         int apk2 = APP2_CERT1_CERT2;
         String apk1Name = "install1.apk";
         String apk2Name = "install2.apk";
-        InstallParams ip1 = null;
 
+        final InstallParams ip = installFromRawResource(apk1Name, apk1, 0, false,
+                false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
         try {
-            ip1 = installFromRawResource(apk1Name, apk1, 0, false,
-                    false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
             PackageManager pm = mContext.getPackageManager();
             // Delete app2
             File filesDir = mContext.getFilesDir();
@@ -3480,12 +3485,10 @@
             getPm().deletePackage(pkg.packageName, null, PackageManager.DELETE_ALL_USERS);
             // Check signatures now
             int match = mContext.getPackageManager().checkSignatures(
-                    ip1.pkg.packageName, pkg.packageName);
+                    ip.pkg.packageName, pkg.packageName);
             assertEquals(PackageManager.SIGNATURE_UNKNOWN_PACKAGE, match);
         } finally {
-            if (ip1 != null) {
-                cleanUpInstall(ip1);
-            }
+            cleanUpInstall(ip);
         }
     }
 
@@ -3493,14 +3496,10 @@
     public void testInstallNoCertificates() throws Exception {
         int apk1 = APP1_UNSIGNED;
         String apk1Name = "install1.apk";
-        InstallParams ip1 = null;
 
-        try {
-            installFromRawResource(apk1Name, apk1, 0, false,
-                    true, PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES,
-                    PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
-        } finally {
-        }
+        installFromRawResource(apk1Name, apk1, 0, false,
+                true, PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES,
+                PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
     }
 
     /*
@@ -3766,35 +3765,43 @@
      * Test that getInstalledPackages returns all the data specified in flags.
      */
     public void testGetInstalledPackagesAll() throws Exception {
-        int flags = PackageManager.GET_ACTIVITIES | PackageManager.GET_GIDS
+        final int flags = PackageManager.GET_ACTIVITIES | PackageManager.GET_GIDS
                 | PackageManager.GET_CONFIGURATIONS | PackageManager.GET_INSTRUMENTATION
                 | PackageManager.GET_PERMISSIONS | PackageManager.GET_PROVIDERS
                 | PackageManager.GET_RECEIVERS | PackageManager.GET_SERVICES
                 | PackageManager.GET_SIGNATURES | PackageManager.GET_UNINSTALLED_PACKAGES;
 
-        List<PackageInfo> packages = getPm().getInstalledPackages(flags);
-        assertNotNull("installed packages cannot be null", packages);
-        assertTrue("installed packages cannot be empty", packages.size() > 0);
+        final InstallParams ip =
+                installFromRawResource("install.apk", R.raw.install_complete_package_info,
+                        0 /*flags*/, false /*cleanUp*/, false /*fail*/, -1 /*result*/,
+                        PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
+        try {
+            final List<PackageInfo> packages = getPm().getInstalledPackages(flags);
+            assertNotNull("installed packages cannot be null", packages);
+            assertTrue("installed packages cannot be empty", packages.size() > 0);
 
-        PackageInfo packageInfo = null;
+            PackageInfo packageInfo = null;
 
-        // Find the package with all components specified in the AndroidManifest
-        // to ensure no null values
-        for (PackageInfo pi : packages) {
-            if ("com.android.frameworks.coretests.install_complete_package_info"
-                    .equals(pi.packageName)) {
-                packageInfo = pi;
-                break;
+            // Find the package with all components specified in the AndroidManifest
+            // to ensure no null values
+            for (PackageInfo pi : packages) {
+                if ("com.android.frameworks.coretests.install_complete_package_info"
+                        .equals(pi.packageName)) {
+                    packageInfo = pi;
+                    break;
+                }
             }
+            assertNotNull("activities should not be null", packageInfo.activities);
+            assertNotNull("configPreferences should not be null", packageInfo.configPreferences);
+            assertNotNull("instrumentation should not be null", packageInfo.instrumentation);
+            assertNotNull("permissions should not be null", packageInfo.permissions);
+            assertNotNull("providers should not be null", packageInfo.providers);
+            assertNotNull("receivers should not be null", packageInfo.receivers);
+            assertNotNull("services should not be null", packageInfo.services);
+            assertNotNull("signatures should not be null", packageInfo.signatures);
+        } finally {
+            cleanUpInstall(ip);
         }
-        assertNotNull("activities should not be null", packageInfo.activities);
-        assertNotNull("configPreferences should not be null", packageInfo.configPreferences);
-        assertNotNull("instrumentation should not be null", packageInfo.instrumentation);
-        assertNotNull("permissions should not be null", packageInfo.permissions);
-        assertNotNull("providers should not be null", packageInfo.providers);
-        assertNotNull("receivers should not be null", packageInfo.receivers);
-        assertNotNull("services should not be null", packageInfo.services);
-        assertNotNull("signatures should not be null", packageInfo.signatures);
     }
 
     /**
@@ -3802,38 +3809,52 @@
      * flags when the GET_UNINSTALLED_PACKAGES flag is set.
      */
     public void testGetUnInstalledPackagesAll() throws Exception {
-        int flags = PackageManager.GET_UNINSTALLED_PACKAGES
+        final int flags = PackageManager.GET_UNINSTALLED_PACKAGES
                 | PackageManager.GET_ACTIVITIES | PackageManager.GET_GIDS
                 | PackageManager.GET_CONFIGURATIONS | PackageManager.GET_INSTRUMENTATION
                 | PackageManager.GET_PERMISSIONS | PackageManager.GET_PROVIDERS
                 | PackageManager.GET_RECEIVERS | PackageManager.GET_SERVICES
                 | PackageManager.GET_SIGNATURES | PackageManager.GET_UNINSTALLED_PACKAGES;
 
-        List<PackageInfo> packages = getPm().getInstalledPackages(flags);
-        assertNotNull("installed packages cannot be null", packages);
-        assertTrue("installed packages cannot be empty", packages.size() > 0);
+        // first, install the package
+        final InstallParams ip =
+                installFromRawResource("install.apk", R.raw.install_complete_package_info,
+                        0 /*flags*/, false /*cleanUp*/, false /*fail*/, -1 /*result*/,
+                        PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
+        try {
+            // then, remove it, keeping it's data around
+            final GenericReceiver receiver = new DeleteReceiver(ip.pkg.packageName);
+            invokeDeletePackage(ip.pkg.packageName, PackageManager.DELETE_KEEP_DATA, receiver);
 
-        PackageInfo packageInfo = null;
+            final List<PackageInfo> packages = getPm().getInstalledPackages(flags);
+            assertNotNull("installed packages cannot be null", packages);
+            assertTrue("installed packages cannot be empty", packages.size() > 0);
 
-        // Find the package with all components specified in the AndroidManifest
-        // to ensure no null values
-        for (PackageInfo pi : packages) {
-            if ("com.android.frameworks.coretests.install_complete_package_info"
-                    .equals(pi.packageName)) {
-                packageInfo = pi;
-                break;
+            PackageInfo packageInfo = null;
+
+            // Find the package with all components specified in the AndroidManifest
+            // to ensure no null values
+            for (PackageInfo pi : packages) {
+                if ("com.android.frameworks.coretests.install_complete_package_info"
+                        .equals(pi.packageName)) {
+                    packageInfo = pi;
+                    break;
+                }
             }
+            assertNotNull("activities should not be null", packageInfo.activities);
+            assertNotNull("configPreferences should not be null", packageInfo.configPreferences);
+            assertNotNull("instrumentation should not be null", packageInfo.instrumentation);
+            assertNotNull("permissions should not be null", packageInfo.permissions);
+            assertNotNull("providers should not be null", packageInfo.providers);
+            assertNotNull("receivers should not be null", packageInfo.receivers);
+            assertNotNull("services should not be null", packageInfo.services);
+            assertNotNull("signatures should not be null", packageInfo.signatures);
+        } finally {
+            cleanUpInstall(ip);
         }
-        assertNotNull("activities should not be null", packageInfo.activities);
-        assertNotNull("configPreferences should not be null", packageInfo.configPreferences);
-        assertNotNull("instrumentation should not be null", packageInfo.instrumentation);
-        assertNotNull("permissions should not be null", packageInfo.permissions);
-        assertNotNull("providers should not be null", packageInfo.providers);
-        assertNotNull("receivers should not be null", packageInfo.receivers);
-        assertNotNull("services should not be null", packageInfo.services);
-        assertNotNull("signatures should not be null", packageInfo.signatures);
     }
 
+    @Suppress
     public void testInstall_BadDex_CleanUp() throws Exception {
         int retCode = PackageManager.INSTALL_FAILED_DEXOPT;
         installFromRawResource("install.apk", R.raw.install_bad_dex, 0, true, true, retCode,
diff --git a/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java b/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java
index c5e2ae6..ddbdc87 100644
--- a/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java
@@ -16,7 +16,11 @@
 
 package android.widget;
 
+import static android.widget.espresso.TextViewActions.mouseDoubleClickOnTextAtIndex;
+import static android.widget.espresso.TextViewActions.mouseLongClickOnTextAtIndex;
+import static android.widget.espresso.TextViewActions.mouseDoubleClickAndDragOnText;
 import static android.widget.espresso.TextViewActions.mouseDragOnText;
+import static android.widget.espresso.TextViewActions.mouseLongClickAndDragOnText;
 import static android.widget.espresso.TextViewAssertions.hasSelection;
 import static android.support.test.espresso.Espresso.onView;
 import static android.support.test.espresso.action.ViewActions.click;
@@ -62,4 +66,104 @@
 
         onView(withId(R.id.textview)).check(hasSelection("llo wor"));
     }
+
+    @SmallTest
+    public void testSelectTextByLongClick() throws Exception {
+        getActivity();
+
+        final String helloWorld = "Hello world!";
+        onView(withId(R.id.textview)).perform(click());
+        onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(helloWorld));
+
+        onView(withId(R.id.textview)).perform(mouseLongClickOnTextAtIndex(0));
+        onView(withId(R.id.textview)).check(hasSelection("Hello"));
+
+        onView(withId(R.id.textview)).perform(mouseLongClickOnTextAtIndex(
+                helloWorld.indexOf("world")));
+        onView(withId(R.id.textview)).check(hasSelection("world"));
+
+        onView(withId(R.id.textview)).perform(mouseLongClickOnTextAtIndex(
+                helloWorld.indexOf("llo")));
+        onView(withId(R.id.textview)).check(hasSelection("Hello"));
+
+        onView(withId(R.id.textview)).perform(mouseLongClickOnTextAtIndex(
+                helloWorld.indexOf("rld")));
+        onView(withId(R.id.textview)).check(hasSelection("world"));
+    }
+
+    @SmallTest
+    public void testSelectTextByDoubleClick() throws Exception {
+        getActivity();
+
+        final String helloWorld = "Hello world!";
+        onView(withId(R.id.textview)).perform(click());
+        onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(helloWorld));
+
+        onView(withId(R.id.textview)).perform(mouseDoubleClickOnTextAtIndex(0));
+        onView(withId(R.id.textview)).check(hasSelection("Hello"));
+
+        onView(withId(R.id.textview)).perform(mouseDoubleClickOnTextAtIndex(
+                helloWorld.indexOf("world")));
+        onView(withId(R.id.textview)).check(hasSelection("world"));
+
+        onView(withId(R.id.textview)).perform(mouseDoubleClickOnTextAtIndex(
+                helloWorld.indexOf("llo")));
+        onView(withId(R.id.textview)).check(hasSelection("Hello"));
+
+        onView(withId(R.id.textview)).perform(mouseDoubleClickOnTextAtIndex(
+                helloWorld.indexOf("rld")));
+        onView(withId(R.id.textview)).check(hasSelection("world"));
+    }
+
+    @SmallTest
+    public void testSelectTextByDoubleClickAndDrag() throws Exception {
+        getActivity();
+
+        final String text = "abcd efg hijk lmn";
+        onView(withId(R.id.textview)).perform(click());
+        onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(text));
+
+        onView(withId(R.id.textview)).perform(
+                mouseDoubleClickAndDragOnText(text.indexOf("f"), text.indexOf("j")));
+        onView(withId(R.id.textview)).check(hasSelection("efg hijk"));
+    }
+
+    @SmallTest
+    public void testSelectTextByDoubleClickAndDrag_reverse() throws Exception {
+        getActivity();
+
+        final String text = "abcd efg hijk lmn";
+        onView(withId(R.id.textview)).perform(click());
+        onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(text));
+
+        onView(withId(R.id.textview)).perform(
+                mouseDoubleClickAndDragOnText(text.indexOf("j"), text.indexOf("f")));
+        onView(withId(R.id.textview)).check(hasSelection("efg hijk"));
+    }
+
+    @SmallTest
+    public void testSelectTextByLongPressAndDrag() throws Exception {
+        getActivity();
+
+        final String text = "abcd efg hijk lmn";
+        onView(withId(R.id.textview)).perform(click());
+        onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(text));
+
+        onView(withId(R.id.textview)).perform(
+                mouseLongClickAndDragOnText(text.indexOf("f"), text.indexOf("j")));
+        onView(withId(R.id.textview)).check(hasSelection("efg hijk"));
+    }
+
+    @SmallTest
+    public void testSelectTextByLongPressAndDrag_reverse() throws Exception {
+        getActivity();
+
+        final String text = "abcd efg hijk lmn";
+        onView(withId(R.id.textview)).perform(click());
+        onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(text));
+
+        onView(withId(R.id.textview)).perform(
+                mouseLongClickAndDragOnText(text.indexOf("j"), text.indexOf("f")));
+        onView(withId(R.id.textview)).check(hasSelection("efg hijk"));
+    }
 }
diff --git a/core/tests/coretests/src/android/widget/espresso/DragAction.java b/core/tests/coretests/src/android/widget/espresso/DragAction.java
index 1132ce0..ce97568 100644
--- a/core/tests/coretests/src/android/widget/espresso/DragAction.java
+++ b/core/tests/coretests/src/android/widget/espresso/DragAction.java
@@ -91,6 +91,72 @@
         },
 
         /**
+         * Starts a drag with a mouse double click.
+         */
+        MOUSE_DOUBLE_CLICK {
+            private DownMotionPerformer downMotion = new DownMotionPerformer() {
+                @Override
+                @Nullable
+                public MotionEvent perform(
+                        UiController uiController,  float[] coordinates, float[] precision) {
+                    return performDoubleTap(uiController, coordinates, precision);
+                }
+            };
+
+            @Override
+            public Status sendSwipe(
+                    UiController uiController,
+                    float[] startCoordinates, float[] endCoordinates, float[] precision) {
+                return sendLinearDrag(
+                        uiController, downMotion, startCoordinates, endCoordinates, precision);
+            }
+
+            @Override
+            public String toString() {
+                return "mouse double click and drag to select";
+            }
+
+            @Override
+            public UiController wrapUiController(UiController uiController) {
+                return new MouseUiController(uiController);
+            }
+        },
+
+        /**
+         * Starts a drag with a mouse long click.
+         */
+        MOUSE_LONG_CLICK {
+            private DownMotionPerformer downMotion = new DownMotionPerformer() {
+                @Override
+                public MotionEvent perform(
+                        UiController uiController, float[] coordinates, float[] precision) {
+                    MotionEvent downEvent = MotionEvents.sendDown(
+                            uiController, coordinates, precision)
+                            .down;
+                    return performLongPress(uiController, coordinates, precision);
+                }
+            };
+
+            @Override
+            public Status sendSwipe(
+                    UiController uiController,
+                    float[] startCoordinates, float[] endCoordinates, float[] precision) {
+                return sendLinearDrag(
+                        uiController, downMotion, startCoordinates, endCoordinates, precision);
+            }
+
+            @Override
+            public String toString() {
+                return "mouse long click and drag to select";
+            }
+
+            @Override
+            public UiController wrapUiController(UiController uiController) {
+                return new MouseUiController(uiController);
+            }
+        },
+
+        /**
          * Starts a drag with a tap.
          */
         TAP {
@@ -127,15 +193,7 @@
                 @Override
                 public MotionEvent perform(
                         UiController uiController, float[] coordinates, float[] precision) {
-                    MotionEvent downEvent = MotionEvents.sendDown(
-                            uiController, coordinates, precision)
-                            .down;
-                    // Duration before a press turns into a long press.
-                    // Factor 1.5 is needed, otherwise a long press is not safely detected.
-                    // See android.test.TouchUtils longClickView
-                    long longPressTimeout = (long) (ViewConfiguration.getLongPressTimeout() * 1.5f);
-                    uiController.loopMainThreadForAtLeast(longPressTimeout);
-                    return downEvent;
+                    return performLongPress(uiController, coordinates, precision);
                 }
             };
 
@@ -162,25 +220,7 @@
                 @Nullable
                 public MotionEvent perform(
                         UiController uiController,  float[] coordinates, float[] precision) {
-                    MotionEvent downEvent = MotionEvents.sendDown(
-                            uiController, coordinates, precision)
-                            .down;
-                    try {
-                        if (!MotionEvents.sendUp(uiController, downEvent)) {
-                            String logMessage = "Injection of up event as part of the double tap " +
-                                    "failed. Sending cancel event.";
-                            Log.d(TAG, logMessage);
-                            MotionEvents.sendCancel(uiController, downEvent);
-                            return null;
-                        }
-
-                        long doubleTapMinimumTimeout = ViewConfiguration.getDoubleTapMinTime();
-                        uiController.loopMainThreadForAtLeast(doubleTapMinimumTimeout);
-
-                        return MotionEvents.sendDown(uiController, coordinates, precision).down;
-                    } finally {
-                        downEvent.recycle();
-                    }
+                    return performDoubleTap(uiController, coordinates, precision);
                 }
             };
 
@@ -267,6 +307,43 @@
             return res;
         }
 
+        private static MotionEvent performLongPress(
+                UiController uiController, float[] coordinates, float[] precision) {
+            MotionEvent downEvent = MotionEvents.sendDown(
+                    uiController, coordinates, precision)
+                    .down;
+            // Duration before a press turns into a long press.
+            // Factor 1.5 is needed, otherwise a long press is not safely detected.
+            // See android.test.TouchUtils longClickView
+            long longPressTimeout = (long) (ViewConfiguration.getLongPressTimeout() * 1.5f);
+            uiController.loopMainThreadForAtLeast(longPressTimeout);
+            return downEvent;
+        }
+
+        @Nullable
+        private static MotionEvent performDoubleTap(
+                UiController uiController,  float[] coordinates, float[] precision) {
+            MotionEvent downEvent = MotionEvents.sendDown(
+                    uiController, coordinates, precision)
+                    .down;
+            try {
+                if (!MotionEvents.sendUp(uiController, downEvent)) {
+                    String logMessage = "Injection of up event as part of the double tap " +
+                            "failed. Sending cancel event.";
+                    Log.d(TAG, logMessage);
+                    MotionEvents.sendCancel(uiController, downEvent);
+                    return null;
+                }
+
+                long doubleTapMinimumTimeout = ViewConfiguration.getDoubleTapMinTime();
+                uiController.loopMainThreadForAtLeast(doubleTapMinimumTimeout);
+
+                return MotionEvents.sendDown(uiController, coordinates, precision).down;
+            } finally {
+                downEvent.recycle();
+            }
+        }
+
         @Override
         public UiController wrapUiController(UiController uiController) {
             return uiController;
diff --git a/core/tests/coretests/src/android/widget/espresso/MouseClickAction.java b/core/tests/coretests/src/android/widget/espresso/MouseClickAction.java
new file mode 100644
index 0000000..de640ca
--- /dev/null
+++ b/core/tests/coretests/src/android/widget/espresso/MouseClickAction.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.widget.espresso;
+
+import org.hamcrest.Matcher;
+
+import android.support.test.espresso.UiController;
+import android.support.test.espresso.ViewAction;
+import android.support.test.espresso.action.CoordinatesProvider;
+import android.support.test.espresso.action.GeneralClickAction;
+import android.support.test.espresso.action.PrecisionDescriber;
+import android.support.test.espresso.action.Tapper;
+import android.view.View;
+
+/**
+ * ViewAction for performing an click on View by a mouse.
+ */
+public final class MouseClickAction implements ViewAction {
+    private final GeneralClickAction mGeneralClickAction;
+
+    public MouseClickAction(Tapper tapper, CoordinatesProvider coordinatesProvider,
+            PrecisionDescriber precisionDescriber) {
+        mGeneralClickAction = new GeneralClickAction(tapper, coordinatesProvider,
+                precisionDescriber);
+    }
+
+    @Override
+    public Matcher<View> getConstraints() {
+        return mGeneralClickAction.getConstraints();
+    }
+
+    @Override
+    public String getDescription() {
+        return mGeneralClickAction.getDescription();
+    }
+
+    @Override
+    public void perform(UiController uiController, View view) {
+        mGeneralClickAction.perform(new MouseUiController(uiController), view);
+    }
+}
diff --git a/core/tests/coretests/src/android/widget/espresso/TextViewActions.java b/core/tests/coretests/src/android/widget/espresso/TextViewActions.java
index 32abc86..32cc6d6 100644
--- a/core/tests/coretests/src/android/widget/espresso/TextViewActions.java
+++ b/core/tests/coretests/src/android/widget/espresso/TextViewActions.java
@@ -53,6 +53,21 @@
     }
 
     /**
+     * Returns an action that clicks by mouse on text at an index on the TextView.<br>
+     * <br>
+     * View constraints:
+     * <ul>
+     * <li>must be a TextView displayed on screen
+     * <ul>
+     *
+     * @param index The index of the TextView's text to click on.
+     */
+    public static ViewAction mouseClickOnTextAtIndex(int index) {
+        return actionWithAssertions(
+                new MouseClickAction(Tap.SINGLE, new TextCoordinates(index), Press.PINPOINT));
+    }
+
+    /**
      * Returns an action that double-clicks on text at an index on the TextView.<br>
      * <br>
      * View constraints:
@@ -68,6 +83,21 @@
     }
 
     /**
+     * Returns an action that double-clicks by mouse on text at an index on the TextView.<br>
+     * <br>
+     * View constraints:
+     * <ul>
+     * <li>must be a TextView displayed on screen
+     * <ul>
+     *
+     * @param index The index of the TextView's text to double-click on.
+     */
+    public static ViewAction mouseDoubleClickOnTextAtIndex(int index) {
+        return actionWithAssertions(
+                new MouseClickAction(Tap.DOUBLE, new TextCoordinates(index), Press.PINPOINT));
+    }
+
+    /**
      * Returns an action that long presses on text at an index on the TextView.<br>
      * <br>
      * View constraints:
@@ -83,6 +113,21 @@
     }
 
     /**
+     * Returns an action that long click by mouse on text at an index on the TextView.<br>
+     * <br>
+     * View constraints:
+     * <ul>
+     * <li>must be a TextView displayed on screen
+     * <ul>
+     *
+     * @param index The index of the TextView's text to long click on.
+     */
+    public static ViewAction mouseLongClickOnTextAtIndex(int index) {
+        return actionWithAssertions(
+                new MouseClickAction(Tap.LONG, new TextCoordinates(index), Press.PINPOINT));
+    }
+
+    /**
      * Returns an action that long presses then drags on text from startIndex to endIndex on the
      * TextView.<br>
      * <br>
@@ -148,6 +193,50 @@
                         TextView.class));
     }
 
+    /**
+     * Returns an action that double click then drags by mouse on text from startIndex to endIndex
+     * on the TextView.<br>
+     * <br>
+     * View constraints:
+     * <ul>
+     * <li>must be a TextView displayed on screen
+     * <ul>
+     *
+     * @param startIndex The index of the TextView's text to start a drag from
+     * @param endIndex The index of the TextView's text to end the drag at
+     */
+    public static ViewAction mouseDoubleClickAndDragOnText(int startIndex, int endIndex) {
+        return actionWithAssertions(
+                new DragAction(
+                        DragAction.Drag.MOUSE_DOUBLE_CLICK,
+                        new TextCoordinates(startIndex),
+                        new TextCoordinates(endIndex),
+                        Press.PINPOINT,
+                        TextView.class));
+    }
+
+    /**
+     * Returns an action that long click then drags by mouse on text from startIndex to endIndex
+     * on the TextView.<br>
+     * <br>
+     * View constraints:
+     * <ul>
+     * <li>must be a TextView displayed on screen
+     * <ul>
+     *
+     * @param startIndex The index of the TextView's text to start a drag from
+     * @param endIndex The index of the TextView's text to end the drag at
+     */
+    public static ViewAction mouseLongClickAndDragOnText(int startIndex, int endIndex) {
+        return actionWithAssertions(
+                new DragAction(
+                        DragAction.Drag.MOUSE_LONG_CLICK,
+                        new TextCoordinates(startIndex),
+                        new TextCoordinates(endIndex),
+                        Press.PINPOINT,
+                        TextView.class));
+    }
+
     public enum Handle {
         SELECTION_START,
         SELECTION_END,
@@ -226,9 +315,25 @@
                     (new TextCoordinates(mIndex)).calculateCoordinates(mTextView);
             final Rect bounds = new Rect();
             view.getBoundsOnScreen(bounds);
-            final float diffX = bounds.centerX() - currentCoordinates[0];
+            final Rect visibleDisplayBounds = new Rect();
+            mTextView.getWindowVisibleDisplayFrame(visibleDisplayBounds);
+            visibleDisplayBounds.right -= 1;
+            visibleDisplayBounds.bottom -= 1;
+            if (!visibleDisplayBounds.intersect(bounds)) {
+                throw new PerformException.Builder()
+                        .withActionDescription(mActionDescription
+                                + " The handle is entirely out of the visible display frame of"
+                                + "the TextView's window.")
+                        .withViewDescription(HumanReadables.describe(view))
+                        .build();
+            }
+            final float dragPointX = Math.max(Math.min(bounds.centerX(),
+                    visibleDisplayBounds.right), visibleDisplayBounds.left);
+            final float diffX = dragPointX - currentCoordinates[0];
             final float verticalOffset = bounds.height() * 0.7f;
-            float diffY = bounds.top + verticalOffset - currentCoordinates[1];
+            final float dragPointY = Math.max(Math.min(bounds.top + verticalOffset,
+                    visibleDisplayBounds.bottom), visibleDisplayBounds.top);
+            float diffY = dragPointY - currentCoordinates[1];
             if (currentLine > targetLine) {
                 diffY -= mTextView.getLineHeight() * LINE_SLOP_MULTIPLIER;
             } else if (currentLine < targetLine) {
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/Android.mk
index 78e718f..edeecb2 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/Android.mk
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/Android.mk
@@ -47,9 +47,9 @@
 include $(BUILD_PACKAGE)
 
 ifndef LOCAL_JACK_ENABLED
-$(mainDexList): $(full_classes_proguard_jar) | $(HOST_OUT_EXECUTABLES)/mainDexClasses
+$(mainDexList): $(full_classes_proguard_jar) | $(MAINDEXCLASSES)
 	$(hide) mkdir -p $(dir $@)
-	$(HOST_OUT_EXECUTABLES)/mainDexClasses $< 1>$@
+	$(MAINDEXCLASSES) $< 1>$@
 	echo "com/android/multidexlegacyandexception/Test.class" >> $@
 
 $(built_dex_intermediate): $(mainDexList)
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/Android.mk
index 7c699b6..7e4f0a9 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/Android.mk
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/Android.mk
@@ -47,9 +47,9 @@
 include $(BUILD_PACKAGE)
 
 ifndef LOCAL_JACK_ENABLED
-$(mainDexList): $(full_classes_proguard_jar) | $(HOST_OUT_EXECUTABLES)/mainDexClasses
+$(mainDexList): $(full_classes_proguard_jar) | $(MAINDEXCLASSES)
 	$(hide) mkdir -p $(dir $@)
-	$(HOST_OUT_EXECUTABLES)/mainDexClasses $< 1>$@
+	$(MAINDEXCLASSES) $< 1>$@
 	echo "com/android/multidexlegacytestapp/Test.class" >> $@
 
 $(built_dex_intermediate): $(mainDexList)
@@ -88,9 +88,9 @@
 include $(BUILD_PACKAGE)
 
 ifndef LOCAL_JACK_ENABLED
-$(mainDexList2): $(full_classes_proguard_jar) | $(HOST_OUT_EXECUTABLES)/mainDexClasses
+$(mainDexList2): $(full_classes_proguard_jar) | $(MAINDEXCLASSES)
 	$(hide) mkdir -p $(dir $@)
-	$(HOST_OUT_EXECUTABLES)/mainDexClasses $< 1>$@
+	$(MAINDEXCLASSES) $< 1>$@
 	echo "com/android/multidexlegacytestapp/Test.class" >> $@
 
 $(built_dex_intermediate): $(mainDexList2)
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/Android.mk
index b85c02c..99bcd6c 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/Android.mk
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/Android.mk
@@ -37,9 +37,9 @@
 include $(BUILD_PACKAGE)
 
 ifndef LOCAL_JACK_ENABLED
-$(mainDexList): $(full_classes_proguard_jar) | $(HOST_OUT_EXECUTABLES)/mainDexClasses
+$(mainDexList): $(full_classes_proguard_jar) | $(MAINDEXCLASSES)
 	$(hide) mkdir -p $(dir $@)
-	$(HOST_OUT_EXECUTABLES)/mainDexClasses $< 1>$@
+	$(MAINDEXCLASSES) $< 1>$@
 
 $(built_dex_intermediate): $(mainDexList)
 endif
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/Android.mk
index 0f1d9c0..1c7d807 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/Android.mk
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/Android.mk
@@ -46,9 +46,9 @@
 include $(BUILD_PACKAGE)
 
 ifndef LOCAL_JACK_ENABLED
-$(mainDexList): $(full_classes_proguard_jar) | $(HOST_OUT_EXECUTABLES)/mainDexClasses
+$(mainDexList): $(full_classes_proguard_jar) | $(MAINDEXCLASSES)
 	$(hide) mkdir -p $(dir $@)
-	$(HOST_OUT_EXECUTABLES)/mainDexClasses $< 1>$@
+	$(MAINDEXCLASSES) $< 1>$@
 	echo "com/android/framework/multidexlegacyversionedtestapp/MultiDexUpdateTest.class" >> $@
 
 $(built_dex_intermediate): $(mainDexList)
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/Android.mk
index 67ca483..b77cf31 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/Android.mk
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/Android.mk
@@ -46,9 +46,9 @@
 include $(BUILD_PACKAGE)
 
 ifndef LOCAL_JACK_ENABLED
-$(mainDexList): $(full_classes_proguard_jar) | $(HOST_OUT_EXECUTABLES)/mainDexClasses
+$(mainDexList): $(full_classes_proguard_jar) | $(MAINDEXCLASSES)
 	$(hide) mkdir -p $(dir $@)
-	$(HOST_OUT_EXECUTABLES)/mainDexClasses $< 1>$@
+	$(MAINDEXCLASSES) $< 1>$@
 	echo "com/android/framework/multidexlegacyversionedtestapp/MultiDexUpdateTest.class" >> $@
 
 $(built_dex_intermediate): $(mainDexList)
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/Android.mk
index bf2efb1..3631626 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/Android.mk
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/Android.mk
@@ -46,9 +46,9 @@
 include $(BUILD_PACKAGE)
 
 ifndef LOCAL_JACK_ENABLED
-$(mainDexList): $(full_classes_proguard_jar) | $(HOST_OUT_EXECUTABLES)/mainDexClasses
+$(mainDexList): $(full_classes_proguard_jar) | $(MAINDEXCLASSES)
 	$(hide) mkdir -p $(dir $@)
-	$(HOST_OUT_EXECUTABLES)/mainDexClasses $< 1>$@
+	$(MAINDEXCLASSES) $< 1>$@
 	echo "com/android/framework/multidexlegacyversionedtestapp/MultiDexUpdateTest.class" >> $@
 
 $(built_dex_intermediate): $(mainDexList)
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index ab37519..51019cc 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -126,6 +126,7 @@
     <assign-permission name="android.permission.WAKE_LOCK" uid="media" />
     <assign-permission name="android.permission.UPDATE_DEVICE_STATS" uid="media" />
     <assign-permission name="android.permission.UPDATE_APP_OPS_STATS" uid="media" />
+    <assign-permission name="android.permission.GET_PROCESS_STATE_AND_OOM_SCORE" uid="media" />
 
     <assign-permission name="android.permission.ACCESS_SURFACE_FLINGER" uid="graphics" />
 
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index 95ae72e..5acc1a3 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -484,7 +484,7 @@
      *
      * @param bounds    The maximum size the offscreen bitmap needs to be
      *                  (in local coordinates)
-     * @param alpha     The alpha to apply to the offscreen when when it is
+     * @param alpha     The alpha to apply to the offscreen when it is
                         drawn during restore()
      * @param saveFlags see _SAVE_FLAG constants, generally {@link #ALL_SAVE_FLAG} is recommended
      *                  for performance reasons.
diff --git a/graphics/java/android/graphics/drawable/RippleBackground.java b/graphics/java/android/graphics/drawable/RippleBackground.java
index f9474ef..d313aa5 100644
--- a/graphics/java/android/graphics/drawable/RippleBackground.java
+++ b/graphics/java/android/graphics/drawable/RippleBackground.java
@@ -139,6 +139,7 @@
         exit.setInterpolator(LINEAR_INTERPOLATOR);
         exit.setDuration(OPACITY_EXIT_DURATION);
         exit.setStartDelay(fastEnterDuration);
+        exit.setStartValue(targetAlpha);
         set.add(exit);
 
         // Linear "fast" enter based on current opacity.
diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h
index bd11d0a..e7cc464 100644
--- a/libs/hwui/DisplayListOp.h
+++ b/libs/hwui/DisplayListOp.h
@@ -1383,7 +1383,10 @@
     friend class TestUtils;
 public:
     DrawRenderNodeOp(RenderNode* renderNode, const mat4& transformFromParent, bool clipIsSimple)
-            : DrawBoundedOp(0, 0, renderNode->getWidth(), renderNode->getHeight(), nullptr)
+            : DrawBoundedOp(0, 0,
+                    renderNode->stagingProperties().getWidth(),
+                    renderNode->stagingProperties().getHeight(),
+                    nullptr)
             , renderNode(renderNode)
             , mRecordedWithPotentialStencilClip(!clipIsSimple || !transformFromParent.isSimple())
             , localMatrix(transformFromParent)
diff --git a/libs/hwui/FrameInfoVisualizer.cpp b/libs/hwui/FrameInfoVisualizer.cpp
index b416615..b7dd3b7 100644
--- a/libs/hwui/FrameInfoVisualizer.cpp
+++ b/libs/hwui/FrameInfoVisualizer.cpp
@@ -16,6 +16,7 @@
 #include "FrameInfoVisualizer.h"
 
 #include "OpenGLRenderer.h"
+#include "utils/Color.h"
 
 #include <cutils/compiler.h>
 #include <array>
@@ -27,19 +28,19 @@
 #define PROFILE_DRAW_THRESHOLD_STROKE_WIDTH 2
 #define PROFILE_DRAW_DP_PER_MS 7
 
+namespace android {
+namespace uirenderer {
+
 // Must be NUM_ELEMENTS in size
-static const SkColor THRESHOLD_COLOR = 0xff5faa4d;
-static const SkColor BAR_FAST_ALPHA = 0x8F000000;
-static const SkColor BAR_JANKY_ALPHA = 0xDF000000;
+static const SkColor THRESHOLD_COLOR = Color::Green_500;
+static const SkColor BAR_FAST_MASK = 0x8FFFFFFF;
+static const SkColor BAR_JANKY_MASK = 0xDFFFFFFF;
 
 // We could get this from TimeLord and use the actual frame interval, but
 // this is good enough
 #define FRAME_THRESHOLD 16
 #define FRAME_THRESHOLD_NS 16000000
 
-namespace android {
-namespace uirenderer {
-
 struct BarSegment {
     FrameInfoIndex start;
     FrameInfoIndex end;
@@ -47,13 +48,13 @@
 };
 
 static const std::array<BarSegment,7> Bar {{
-    { FrameInfoIndex::IntendedVsync, FrameInfoIndex::HandleInputStart, 0x00796B },
-    { FrameInfoIndex::HandleInputStart, FrameInfoIndex::PerformTraversalsStart, 0x388E3C },
-    { FrameInfoIndex::PerformTraversalsStart, FrameInfoIndex::DrawStart, 0x689F38},
-    { FrameInfoIndex::DrawStart, FrameInfoIndex::SyncStart, 0x2196F3},
-    { FrameInfoIndex::SyncStart, FrameInfoIndex::IssueDrawCommandsStart, 0x4FC3F7},
-    { FrameInfoIndex::IssueDrawCommandsStart, FrameInfoIndex::SwapBuffers, 0xF44336},
-    { FrameInfoIndex::SwapBuffers, FrameInfoIndex::FrameCompleted, 0xFF9800},
+    { FrameInfoIndex::IntendedVsync, FrameInfoIndex::HandleInputStart, Color::Teal_700 },
+    { FrameInfoIndex::HandleInputStart, FrameInfoIndex::PerformTraversalsStart, Color::Green_700 },
+    { FrameInfoIndex::PerformTraversalsStart, FrameInfoIndex::DrawStart, Color::LightGreen_700 },
+    { FrameInfoIndex::DrawStart, FrameInfoIndex::SyncStart, Color::Blue_500 },
+    { FrameInfoIndex::SyncStart, FrameInfoIndex::IssueDrawCommandsStart, Color::LightBlue_300 },
+    { FrameInfoIndex::IssueDrawCommandsStart, FrameInfoIndex::SwapBuffers, Color::Red_500},
+    { FrameInfoIndex::SwapBuffers, FrameInfoIndex::FrameCompleted, Color::Orange_500},
 }};
 
 static int dpToPx(int dp, float density) {
@@ -197,9 +198,9 @@
     SkPaint paint;
     for (size_t i = 0; i < Bar.size(); i++) {
         nextBarSegment(Bar[i].start, Bar[i].end);
-        paint.setColor(Bar[i].color | BAR_FAST_ALPHA);
+        paint.setColor(Bar[i].color & BAR_FAST_MASK);
         canvas->drawRects(mFastRects.get(), mNumFastRects * 4, &paint);
-        paint.setColor(Bar[i].color | BAR_JANKY_ALPHA);
+        paint.setColor(Bar[i].color & BAR_JANKY_MASK);
         canvas->drawRects(mJankyRects.get(), mNumJankyRects * 4, &paint);
     }
 }
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index 61fa384..69c686e 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -435,8 +435,9 @@
             refPaint(paint), refBitmap(*bitmap)));
 }
 void RecordingCanvas::drawRenderNode(RenderNode* renderNode) {
+    auto&& stagingProps = renderNode->stagingProperties();
     RenderNodeOp* op = new (alloc()) RenderNodeOp(
-            Rect(0, 0, renderNode->getWidth(), renderNode->getHeight()), // are these safe? they're theoretically dynamic
+            Rect(stagingProps.getWidth(), stagingProps.getHeight()),
             *(mState.currentSnapshot()->transform),
             mState.getRenderTargetClipBounds(),
             renderNode);
diff --git a/libs/hwui/tests/scenes/ListViewAnimation.cpp b/libs/hwui/tests/scenes/ListViewAnimation.cpp
new file mode 100644
index 0000000..27adb12
--- /dev/null
+++ b/libs/hwui/tests/scenes/ListViewAnimation.cpp
@@ -0,0 +1,146 @@
+/*
+ * 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 "TestSceneBase.h"
+#include "utils/Color.h"
+
+#include <cstdio>
+
+class ListViewAnimation;
+
+static Benchmark _ListView(BenchmarkInfo{
+    "listview",
+    "A mock ListView of scrolling content. Doesn't re-bind/re-record views as they are recycled, so"
+    "won't upload much content (either glyphs, or bitmaps).",
+    simpleCreateScene<ListViewAnimation>
+});
+
+class ListViewAnimation : public TestScene {
+public:
+    int cardHeight;
+    int cardSpacing;
+    int cardWidth;
+    int cardLeft;
+    sp<RenderNode> listView;
+    std::vector< sp<RenderNode> > cards;
+    void createContent(int width, int height, TestCanvas& canvas) override {
+        srand(0);
+        cardHeight = dp(60);
+        cardSpacing = dp(16);
+        cardWidth = std::min((height - cardSpacing * 2), (int)dp(300));
+        cardLeft = (width - cardWidth) / 2;
+
+        for (int y = 0; y < height + (cardHeight + cardSpacing - 1); y += (cardHeight + cardSpacing)) {
+            cards.push_back(createCard(cards.size(), y));
+        }
+        listView = TestUtils::createNode(0, 0, width, height,
+                [this](RenderProperties& props, TestCanvas& canvas) {
+            for (size_t ci = 0; ci < cards.size(); ci++) {
+                canvas.drawRenderNode(cards[ci].get());
+            }
+        });
+
+        canvas.drawColor(Color::Grey_500, SkXfermode::kSrcOver_Mode);
+        canvas.drawRenderNode(listView.get());
+    }
+
+    void doFrame(int frameNr) override {
+        int scrollPx = dp(frameNr) * 3;
+        int cardIndexOffset = scrollPx / (cardSpacing + cardHeight);
+        int pxOffset = -(scrollPx % (cardSpacing + cardHeight));
+
+        TestCanvas canvas(cardWidth, cardHeight);
+        for (size_t ci = 0; ci < cards.size(); ci++) {
+            // update card position
+            auto card = cards[(ci + cardIndexOffset) % cards.size()];
+            int top = ((int)ci) * (cardSpacing + cardHeight) + pxOffset;
+            card->mutateStagingProperties().setLeftTopRightBottom(
+                    cardLeft, top, cardLeft + cardWidth, top + cardHeight);
+            card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+
+            // draw it to parent DisplayList
+            canvas.drawRenderNode(cards[ci].get());
+        }
+        listView->setStagingDisplayList(canvas.finishRecording());
+    }
+private:
+    SkBitmap createRandomCharIcon() {
+        int size = cardHeight - (dp(10) * 2);
+        SkBitmap bitmap = TestUtils::createSkBitmap(size, size);
+        SkCanvas canvas(bitmap);
+        canvas.clear(0);
+
+        SkPaint paint;
+        paint.setAntiAlias(true);
+        SkColor randomColor = BrightColors[rand() % BrightColorsCount];
+        paint.setColor(randomColor);
+        canvas.drawCircle(size / 2, size / 2, size / 2, paint);
+
+        bool bgDark = SkColorGetR(randomColor) + SkColorGetG(randomColor) + SkColorGetB(randomColor)
+                < 128 * 3;
+        paint.setColor(bgDark ? Color::White : Color::Grey_700);
+        paint.setTextAlign(SkPaint::kCenter_Align);
+        paint.setTextSize(size / 2);
+        char charToShow = 'A' + (rand() % 26);
+        canvas.drawText(&charToShow, 1, size / 2, /*approximate centering*/ size * 0.7, paint);
+        return bitmap;
+    }
+
+    static SkBitmap createBoxBitmap(bool filled) {
+        int size = dp(20);
+        int stroke = dp(2);
+        SkBitmap bitmap = TestUtils::createSkBitmap(size, size);
+        SkCanvas canvas(bitmap);
+        canvas.clear(Color::Transparent);
+
+        SkPaint paint;
+        paint.setAntiAlias(true);
+        paint.setColor(filled ? Color::Yellow_500 : Color::Grey_700);
+        paint.setStyle(filled ? SkPaint::kStrokeAndFill_Style : SkPaint::kStroke_Style);
+        paint.setStrokeWidth(stroke);
+        canvas.drawRect(SkRect::MakeLTRB(stroke, stroke, size - stroke, size - stroke), paint);
+        return bitmap;
+    }
+
+    sp<RenderNode> createCard(int cardId, int top) {
+        return TestUtils::createNode(cardLeft, top, cardLeft + cardWidth, top + cardHeight,
+                [this, cardId](RenderProperties& props, TestCanvas& canvas) {
+            static SkBitmap filledBox = createBoxBitmap(true);
+            static SkBitmap strokedBox = createBoxBitmap(false);
+
+            props.mutableOutline().setRoundRect(0, 0, cardWidth, cardHeight, dp(6), 1);
+            props.mutableOutline().setShouldClip(true);
+            canvas.drawColor(Color::White, SkXfermode::kSrcOver_Mode);
+
+            SkPaint textPaint;
+            textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+            textPaint.setColor(rand() % 2 ? Color::Black : Color::Grey_500);
+            textPaint.setTextSize(dp(20));
+            textPaint.setAntiAlias(true);
+            char buf[256];
+            snprintf(buf, sizeof(buf), "This card is #%d", cardId);
+            TestUtils::drawTextToCanvas(&canvas, buf, textPaint, cardHeight, dp(25));
+            textPaint.setTextSize(dp(15));
+            TestUtils::drawTextToCanvas(&canvas, "This is some more text on the card", textPaint,
+                    cardHeight, dp(45));
+
+            canvas.drawBitmap(createRandomCharIcon(), dp(10), dp(10), nullptr);
+
+            const SkBitmap& boxBitmap = rand() % 2 ? filledBox : strokedBox;
+            canvas.drawBitmap(boxBitmap, cardWidth - dp(10) - boxBitmap.width(), dp(10), nullptr);
+        });
+    }
+};
diff --git a/libs/hwui/tests/scenes/RecentsAnimation.cpp b/libs/hwui/tests/scenes/RecentsAnimation.cpp
index 1e38d84..5d4ef96 100644
--- a/libs/hwui/tests/scenes/RecentsAnimation.cpp
+++ b/libs/hwui/tests/scenes/RecentsAnimation.cpp
@@ -15,6 +15,7 @@
  */
 
 #include "TestSceneBase.h"
+#include "utils/Color.h"
 
 class RecentsAnimation;
 
@@ -29,16 +30,16 @@
 public:
     void createContent(int width, int height, TestCanvas& renderer) override {
         static SkColor COLORS[] = {
-                0xFFF44336,
-                0xFF9C27B0,
-                0xFF2196F3,
-                0xFF4CAF50,
+                Color::Red_500,
+                Color::Purple_500,
+                Color::Blue_500,
+                Color::Green_500,
         };
 
         thumbnailSize = std::min(std::min(width, height) / 2, 720);
         int cardsize = std::min(width, height) - dp(64);
 
-        renderer.drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
+        renderer.drawColor(Color::White, SkXfermode::kSrcOver_Mode);
         renderer.insertReorderBarrier(true);
 
         int x = dp(32);
@@ -63,7 +64,7 @@
             mCards[ci]->setPropertyFieldsDirty(RenderNode::Y);
         }
         mThumbnail.eraseColor(TestUtils::interpolateColor(
-                curFrame / 150.0f, 0xFF4CAF50, 0xFFFF5722));
+                curFrame / 150.0f, Color::Green_500, Color::DeepOrange_500));
     }
 
 private:
@@ -75,7 +76,7 @@
             props.mutableOutline().setRoundRect(0, 0, width, height, dp(10), 1);
             props.mutableOutline().setShouldClip(true);
 
-            canvas.drawColor(0xFFEEEEEE, SkXfermode::kSrcOver_Mode);
+            canvas.drawColor(Color::Grey_200, SkXfermode::kSrcOver_Mode);
             canvas.drawBitmap(thumb, 0, 0, thumb.width(), thumb.height(),
                     0, 0, width, height, nullptr);
         });
diff --git a/libs/hwui/utils/Color.h b/libs/hwui/utils/Color.h
new file mode 100644
index 0000000..b5157f4
--- /dev/null
+++ b/libs/hwui/utils/Color.h
@@ -0,0 +1,86 @@
+/*
+ * 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 COLOR_H
+#define COLOR_H
+
+#include <SkColor.h>
+
+namespace android {
+namespace uirenderer {
+    namespace Color {
+        enum Color {
+            Red_500 = 0xFFF44336,
+            Pink_500 = 0xFFE91E63,
+            Purple_500 = 0xFF9C27B0,
+            DeepPurple_500 = 0xFF673AB7,
+            Indigo_500 = 0xFF3F51B5,
+            Blue_500 = 0xFF2196F3,
+            LightBlue_300 = 0xFF4FC3F7,
+            LightBlue_500 = 0xFF03A9F4,
+            Cyan_500 = 0xFF00BCD4,
+            Teal_500 = 0xFF009688,
+            Teal_700 = 0xFF00796B,
+            Green_500 = 0xFF4CAF50,
+            Green_700 = 0xFF388E3C,
+            LightGreen_500 = 0xFF8BC34A,
+            LightGreen_700 = 0xFF689F38,
+            Lime_500 = 0xFFCDDC39,
+            Yellow_500 = 0xFFFFEB3B,
+            Amber_500 = 0xFFFFC107,
+            Orange_500 = 0xFFFF9800,
+            DeepOrange_500 = 0xFFFF5722,
+            Brown_500 = 0xFF795548,
+            Grey_200 = 0xFFEEEEEE,
+            Grey_500 = 0xFF9E9E9E,
+            Grey_700 = 0xFF616161,
+            BlueGrey_500 = 0xFF607D8B,
+            Transparent = 0x00000000,
+            Black = 0xFF000000,
+            White = 0xFFFFFFFF,
+        };
+    }
+
+    static_assert(Color::White == SK_ColorWHITE, "color format has changed");
+    static_assert(Color::Black == SK_ColorBLACK, "color format has changed");
+
+    // Array of bright (500 intensity) colors for synthetic content
+    static const Color::Color BrightColors[] = {
+        Color::Red_500,
+        Color::Pink_500,
+        Color::Purple_500,
+        Color::DeepPurple_500,
+        Color::Indigo_500,
+        Color::Blue_500,
+        Color::LightBlue_500,
+        Color::Cyan_500,
+        Color::Teal_500,
+        Color::Green_500,
+        Color::LightGreen_500,
+        Color::Lime_500,
+        Color::Yellow_500,
+        Color::Amber_500,
+        Color::Orange_500,
+        Color::DeepOrange_500,
+        Color::Brown_500,
+        Color::Grey_500,
+        Color::BlueGrey_500,
+    };
+    static constexpr int BrightColorsCount = sizeof(BrightColors) / sizeof(Color::Color);
+
+} /* namespace uirenderer */
+} /* namespace android */
+
+#endif /* TEST_UTILS_H */
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 1c2c940..f5b7a2e 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -36,6 +36,7 @@
 import android.os.PowerManager;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.SystemProperties;
 import android.provider.Settings;
 import android.system.ErrnoException;
 import android.system.OsConstants;
@@ -1260,6 +1261,13 @@
      */
     public void setWakeMode(Context context, int mode) {
         boolean washeld = false;
+
+        /* Disable persistant wakelocks in media player based on property */
+        if (SystemProperties.getBoolean("audio.offload.ignore_setawake", false) == true) {
+            Log.w(TAG, "IGNORING setWakeMode " + mode);
+            return;
+        }
+
         if (mWakeLock != null) {
             if (mWakeLock.isHeld()) {
                 washeld = true;
diff --git a/media/java/android/media/browse/MediaBrowser.java b/media/java/android/media/browse/MediaBrowser.java
index 41b8ab2..be9fb47 100644
--- a/media/java/android/media/browse/MediaBrowser.java
+++ b/media/java/android/media/browse/MediaBrowser.java
@@ -240,7 +240,7 @@
     /**
      * Gets the root id.
      * <p>
-     * Note that the root id may become invalid or change when when the
+     * Note that the root id may become invalid or change when the
      * browser is disconnected.
      * </p>
      *
@@ -270,7 +270,7 @@
     /**
      * Gets the media session token associated with the media browser.
      * <p>
-     * Note that the session token may become invalid or change when when the
+     * Note that the session token may become invalid or change when the
      * browser is disconnected.
      * </p>
      *
diff --git a/media/jni/soundpool/SoundPool.cpp b/media/jni/soundpool/SoundPool.cpp
index 090be88..70d651f 100644
--- a/media/jni/soundpool/SoundPool.cpp
+++ b/media/jni/soundpool/SoundPool.cpp
@@ -187,6 +187,17 @@
     return mDecodeThread != NULL;
 }
 
+sp<Sample> SoundPool::findSample(int sampleID)
+{
+    Mutex::Autolock lock(&mLock);
+    return findSample_l(sampleID);
+}
+
+sp<Sample> SoundPool::findSample_l(int sampleID)
+{
+    return mSamples.valueFor(sampleID);
+}
+
 SoundChannel* SoundPool::findChannel(int channelID)
 {
     for (int i = 0; i < mMaxChannels; ++i) {
@@ -211,18 +222,21 @@
 {
     ALOGV("load: fd=%d, offset=%" PRId64 ", length=%" PRId64 ", priority=%d",
             fd, offset, length, priority);
-    Mutex::Autolock lock(&mLock);
-    sp<Sample> sample = new Sample(++mNextSampleID, fd, offset, length);
-    mSamples.add(sample->sampleID(), sample);
-    doLoad(sample);
-    return sample->sampleID();
-}
-
-void SoundPool::doLoad(sp<Sample>& sample)
-{
-    ALOGV("doLoad: loading sample sampleID=%d", sample->sampleID());
-    sample->startLoad();
-    mDecodeThread->loadSample(sample->sampleID());
+    int sampleID;
+    {
+        Mutex::Autolock lock(&mLock);
+        sampleID = ++mNextSampleID;
+        sp<Sample> sample = new Sample(sampleID, fd, offset, length);
+        mSamples.add(sampleID, sample);
+        sample->startLoad();
+    }
+    // mDecodeThread->loadSample() must be called outside of mLock.
+    // mDecodeThread->loadSample() may block on mDecodeThread message queue space;
+    // the message queue emptying may block on SoundPool::findSample().
+    //
+    // It theoretically possible that sample loads might decode out-of-order.
+    mDecodeThread->loadSample(sampleID);
+    return sampleID;
 }
 
 bool SoundPool::unload(int sampleID)
@@ -237,7 +251,6 @@
 {
     ALOGV("play sampleID=%d, leftVolume=%f, rightVolume=%f, priority=%d, loop=%d, rate=%f",
             sampleID, leftVolume, rightVolume, priority, loop, rate);
-    sp<Sample> sample;
     SoundChannel* channel;
     int channelID;
 
@@ -247,7 +260,7 @@
         return 0;
     }
     // is sample ready?
-    sample = findSample(sampleID);
+    sp<Sample> sample(findSample_l(sampleID));
     if ((sample == 0) || (sample->state() != Sample::READY)) {
         ALOGW("  sample %d not READY", sampleID);
         return 0;
diff --git a/media/jni/soundpool/SoundPool.h b/media/jni/soundpool/SoundPool.h
index 4aacf53..98d2fa2 100644
--- a/media/jni/soundpool/SoundPool.h
+++ b/media/jni/soundpool/SoundPool.h
@@ -180,6 +180,7 @@
 
     // called from SoundPoolThread
     void sampleLoaded(int sampleID);
+    sp<Sample> findSample(int sampleID);
 
     // called from AudioTrack thread
     void done_l(SoundChannel* channel);
@@ -191,8 +192,7 @@
 private:
     SoundPool() {} // no default constructor
     bool startThreads();
-    void doLoad(sp<Sample>& sample);
-    sp<Sample> findSample(int sampleID) { return mSamples.valueFor(sampleID); }
+    sp<Sample> findSample_l(int sampleID);
     SoundChannel* findChannel (int channelID);
     SoundChannel* findNextChannel (int channelID);
     SoundChannel* allocateChannel_l(int priority, int sampleID);
diff --git a/packages/DocumentsUI/AndroidManifest.xml b/packages/DocumentsUI/AndroidManifest.xml
index 595928a..60f5d60 100644
--- a/packages/DocumentsUI/AndroidManifest.xml
+++ b/packages/DocumentsUI/AndroidManifest.xml
@@ -39,7 +39,7 @@
         </activity>
 
         <activity
-            android:name=".ManageRootActivity"
+            android:name=".DownloadsActivity"
             android:theme="@style/DocumentsFullScreenTheme"
             android:icon="@drawable/ic_doc_text">
             <intent-filter>
diff --git a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
index 91ac033..abb08f5 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
@@ -260,8 +260,7 @@
         } else if (id == R.id.menu_settings) {
             final RootInfo root = getCurrentRoot();
             final Intent intent = new Intent(DocumentsContract.ACTION_DOCUMENT_ROOT_SETTINGS);
-            intent.setDataAndType(DocumentsContract.buildRootUri(root.authority, root.rootId),
-                    DocumentsContract.Root.MIME_TYPE_ITEM);
+            intent.setDataAndType(root.getUri(), DocumentsContract.Root.MIME_TYPE_ITEM);
             startActivity(intent);
             return true;
         }
diff --git a/packages/DocumentsUI/src/com/android/documentsui/CopyService.java b/packages/DocumentsUI/src/com/android/documentsui/CopyService.java
index 55e2f44..b99c806 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/CopyService.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/CopyService.java
@@ -39,13 +39,15 @@
 import android.os.SystemClock;
 import android.provider.DocumentsContract;
 import android.provider.DocumentsContract.Document;
+import android.support.annotation.Nullable;
+import android.support.annotation.VisibleForTesting;
 import android.support.design.widget.Snackbar;
 import android.text.format.DateUtils;
 import android.util.Log;
-import android.widget.Toast;
 
 import com.android.documentsui.model.DocumentInfo;
 import com.android.documentsui.model.DocumentStack;
+import com.android.documentsui.model.RootInfo;
 
 import libcore.io.IoUtils;
 
@@ -72,6 +74,12 @@
     // TODO: Move it to a shared file when more operations are implemented.
     public static final int FAILURE_COPY = 1;
 
+    // Parameters of the copy job. Requests to an IntentService are serialized so this code only
+    // needs to deal with one job at a time.
+    // NOTE: This must be declared by concrete type as the concrete type
+    // is required by putParcelableArrayListExtra.
+    private final ArrayList<DocumentInfo> mFailedFiles = new ArrayList<>();
+
     private PowerManager mPowerManager;
 
     private NotificationManager mNotificationManager;
@@ -80,9 +88,6 @@
     // Jobs are serialized but a job ID is used, to avoid mixing up cancellation requests.
     private String mJobId;
     private volatile boolean mIsCancelled;
-    // Parameters of the copy job. Requests to an IntentService are serialized so this code only
-    // needs to deal with one job at a time.
-    private final ArrayList<DocumentInfo> mFailedFiles;
     private long mBatchSize;
     private long mBytesCopied;
     private long mStartTime;
@@ -97,10 +102,11 @@
     private ContentProviderClient mSrcClient;
     private ContentProviderClient mDstClient;
 
+    // For testing only.
+    @Nullable private TestOnlyListener mJobFinishedListener;
+
     public CopyService() {
         super("CopyService");
-
-        mFailedFiles = new ArrayList<DocumentInfo>();
     }
 
     /**
@@ -115,7 +121,11 @@
         final Resources res = activity.getResources();
         final Intent copyIntent = new Intent(activity, CopyService.class);
         copyIntent.putParcelableArrayListExtra(
-                EXTRA_SRC_LIST, new ArrayList<DocumentInfo>(srcDocs));
+                EXTRA_SRC_LIST,
+                // Don't create a copy unless absolutely necessary :)
+                srcDocs instanceof ArrayList
+                    ? (ArrayList<DocumentInfo>) srcDocs
+                    : new ArrayList<DocumentInfo>(srcDocs));
         copyIntent.putExtra(Shared.EXTRA_STACK, (Parcelable) dstStack);
         copyIntent.putExtra(EXTRA_TRANSFER_MODE, mode);
 
@@ -198,6 +208,11 @@
                         .setAutoCancel(true);
                 mNotificationManager.notify(mJobId, 0, errorBuilder.build());
             }
+
+            if (mJobFinishedListener != null) {
+                mJobFinishedListener.onFinished(mFailedFiles);
+            }
+
             if (DEBUG) Log.d(TAG, "Done cleaning up");
         }
     }
@@ -269,6 +284,26 @@
     }
 
     /**
+     * Sets a callback to be run when the next run job is finished.
+     * This is test ONLY instrumentation. The alternative is for us to add
+     * broadcast intents SOLELY for the purpose of testing.
+     * @param listener
+     */
+    @VisibleForTesting
+    void addFinishedListener(TestOnlyListener listener) {
+        this.mJobFinishedListener = listener;
+
+    }
+
+    /**
+     * Only used for testing. Is that obvious enough?
+     */
+    @VisibleForTesting
+    interface TestOnlyListener {
+        void onFinished(List<DocumentInfo> failed);
+    }
+
+    /**
      * Calculates the cumulative size of all the documents in the list. Directories are recursed
      * into and totaled up.
      *
@@ -279,7 +314,7 @@
     private long calculateFileSizes(List<DocumentInfo> srcs) throws RemoteException {
         long result = 0;
         for (DocumentInfo src : srcs) {
-            if (Document.MIME_TYPE_DIR.equals(src.mimeType)) {
+            if (src.isDirectory()) {
                 // Directories need to be recursed into.
                 result += calculateFileSizesHelper(src.derivedUri);
             } else {
@@ -412,8 +447,21 @@
      */
     private void copy(DocumentInfo srcInfo, DocumentInfo dstDirInfo, int mode)
             throws RemoteException {
-        if (DEBUG) Log.d(TAG, "Copying " + srcInfo.displayName + " (" + srcInfo.derivedUri + ")" +
-            " to " + dstDirInfo.displayName + " (" + dstDirInfo.derivedUri + ")");
+
+        String opDesc = mode == TRANSFER_MODE_COPY ? "copy" : "move";
+
+        // Guard unsupported recursive operation.
+        if (dstDirInfo.equals(srcInfo) || isDescendentOf(srcInfo, dstDirInfo)) {
+            if (DEBUG) Log.d(TAG,
+                    "Skipping recursive " + opDesc + " of directory " + dstDirInfo.derivedUri);
+            mFailedFiles.add(srcInfo);
+            return;
+        }
+
+        if (DEBUG) Log.d(TAG,
+                "Performing " + opDesc + " of " + srcInfo.displayName
+                + " (" + srcInfo.derivedUri + ")" + " to " + dstDirInfo.displayName
+                + " (" + dstDirInfo.derivedUri + ")");
 
         // When copying within the same provider, try to use optimized copying and moving.
         // If not supported, then fallback to byte-by-byte copy/move.
@@ -450,7 +498,7 @@
             return;
         }
 
-        if (Document.MIME_TYPE_DIR.equals(srcInfo.mimeType)) {
+        if (srcInfo.isDirectory()) {
             copyDirectoryHelper(srcInfo.derivedUri, dstUri, mode);
         } else {
             copyFileHelper(srcInfo.derivedUri, dstUri, mode);
@@ -458,6 +506,17 @@
     }
 
     /**
+     * Returns true if {@code doc} is a descendant of {@code parentDoc}.
+     */
+    boolean isDescendentOf(DocumentInfo doc, DocumentInfo parentDoc) throws RemoteException {
+        if (parentDoc.isDirectory() && doc.authority.equals(parentDoc.authority)) {
+            return DocumentsContract.isChildDocument(
+                    mDstClient, doc.derivedUri, parentDoc.derivedUri);
+        }
+        return false;
+    }
+
+    /**
      * Handles recursion into a directory and copying its contents. Note that in linux terms, this
      * does the equivalent of "cp src/* dst", not "cp -r src dst".
      *
diff --git a/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java
index c6425a6..f3c3f2f 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java
@@ -32,6 +32,7 @@
 import android.os.Bundle;
 import android.provider.DocumentsContract;
 import android.provider.DocumentsContract.Document;
+import android.support.annotation.Nullable;
 import android.support.design.widget.Snackbar;
 import android.util.Log;
 import android.view.KeyEvent;
@@ -83,8 +84,10 @@
         editText.setOnEditorActionListener(
                 new OnEditorActionListener() {
                     @Override
-                    public boolean onEditorAction(TextView view, int actionId, KeyEvent event) {
-                        if (event.getKeyCode() == KeyEvent.KEYCODE_ENTER
+                    public boolean onEditorAction(
+                            TextView view, int actionId, @Nullable KeyEvent event) {
+                        if (event != null
+                                && event.getKeyCode() == KeyEvent.KEYCODE_ENTER
                                 && event.hasNoModifiers()) {
                             createDirectory(editText.getText().toString());
                             dialog.dismiss();
diff --git a/packages/DocumentsUI/src/com/android/documentsui/ManageRootActivity.java b/packages/DocumentsUI/src/com/android/documentsui/DownloadsActivity.java
similarity index 95%
rename from packages/DocumentsUI/src/com/android/documentsui/ManageRootActivity.java
rename to packages/DocumentsUI/src/com/android/documentsui/DownloadsActivity.java
index 3045fa8..f224343 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/ManageRootActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DownloadsActivity.java
@@ -50,8 +50,11 @@
 import java.util.Arrays;
 import java.util.List;
 
-public class ManageRootActivity extends BaseActivity {
-    private static final String TAG = "ManageRootsActivity";
+// Let's face it. MANAGE_ROOT is used almost exclusively
+// for downloads, and is specialized for this purpose.
+// So it is now thusly christened.
+public class DownloadsActivity extends BaseActivity {
+    private static final String TAG = "DownloadsActivity";
 
     private Toolbar mToolbar;
     private Spinner mToolbarStack;
@@ -59,7 +62,7 @@
     private ItemSelectedListener mStackListener;
     private BaseAdapter mStackAdapter;
 
-    public ManageRootActivity() {
+    public DownloadsActivity() {
         super(R.layout.manage_roots_activity, TAG);
     }
 
@@ -250,7 +253,7 @@
         finish();
     }
 
-    public static ManageRootActivity get(Fragment fragment) {
-        return (ManageRootActivity) fragment.getActivity();
+    public static DownloadsActivity get(Fragment fragment) {
+        return (DownloadsActivity) fragment.getActivity();
     }
 }
diff --git a/packages/DocumentsUI/src/com/android/documentsui/FailureDialogFragment.java b/packages/DocumentsUI/src/com/android/documentsui/FailureDialogFragment.java
index 120f610..23074f0 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/FailureDialogFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/FailureDialogFragment.java
@@ -37,7 +37,6 @@
         implements DialogInterface.OnClickListener {
     private static final String TAG = "FailureDialogFragment";
 
-    private int mFailure;
     private int mTransferMode;
     private ArrayList<DocumentInfo> mFailedSrcList;
 
@@ -75,7 +74,6 @@
     public Dialog onCreateDialog(Bundle inState) {
         super.onCreate(inState);
 
-        mFailure = getArguments().getInt(CopyService.EXTRA_FAILURE);
         mTransferMode = getArguments().getInt(CopyService.EXTRA_TRANSFER_MODE);
         mFailedSrcList = getArguments().getParcelableArrayList(CopyService.EXTRA_SRC_LIST);
 
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
index b0421b0..be3013b 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
@@ -741,45 +741,45 @@
 
             Selection selection = mSelectionManager.getSelection(new Selection());
 
-            final int id = item.getItemId();
-            if (id == R.id.menu_open) {
-                openDocuments(selection);
-                mode.finish();
-                return true;
+            switch (item.getItemId()) {
+                case R.id.menu_open:
+                    openDocuments(selection);
+                    mode.finish();
+                    return true;
 
-            } else if (id == R.id.menu_share) {
-                shareDocuments(selection);
-                mode.finish();
-                return true;
+                case R.id.menu_share:
+                    shareDocuments(selection);
+                    mode.finish();
+                    return true;
 
-            } else if (id == R.id.menu_delete) {
-                // Exit selection mode first, so we avoid deselecting deleted documents.
-                mode.finish();
-                deleteDocuments(selection);
-                return true;
+                case R.id.menu_delete:
+                    // Exit selection mode first, so we avoid deselecting deleted documents.
+                    mode.finish();
+                    deleteDocuments(selection);
+                    return true;
 
-            } else if (id == R.id.menu_copy_to) {
-                transferDocuments(selection, CopyService.TRANSFER_MODE_COPY);
-                mode.finish();
-                return true;
+                case R.id.menu_copy_to:
+                    transferDocuments(selection, CopyService.TRANSFER_MODE_COPY);
+                    mode.finish();
+                    return true;
 
-            } else if (id == R.id.menu_move_to) {
-                // Exit selection mode first, so we avoid deselecting deleted documents.
-                mode.finish();
-                transferDocuments(selection, CopyService.TRANSFER_MODE_MOVE);
-                return true;
+                case R.id.menu_move_to:
+                    // Exit selection mode first, so we avoid deselecting deleted documents.
+                    mode.finish();
+                    transferDocuments(selection, CopyService.TRANSFER_MODE_MOVE);
+                    return true;
 
-            } else if (id == R.id.menu_copy_to_clipboard) {
-                copySelectionToClipboard(selection);
-                mode.finish();
-                return true;
+                case R.id.menu_copy_to_clipboard:
+                    copySelectionToClipboard(selection);
+                    return true;
 
-            } else if (id == R.id.menu_select_all) {
-                selectAllFiles();
-                return true;
+                case R.id.menu_select_all:
+                    selectAllFiles();
+                    return true;
 
-            } else {
-                return false;
+                default:
+                    if (DEBUG) Log.d(TAG, "Unhandled menu item selected: " + item);
+                    return false;
             }
         }
     }
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/FragmentTuner.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/FragmentTuner.java
index 38d3805..ef6d2c9 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/FragmentTuner.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/FragmentTuner.java
@@ -176,7 +176,6 @@
             final MenuItem moveTo = menu.findItem(R.id.menu_move_to);
 
             open.setVisible(false);
-            share.setVisible(false);
             delete.setVisible(canDelete);
             copyTo.setVisible(true);
             copyTo.setEnabled(true);
diff --git a/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java b/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java
index cc981e1e..dfdc705 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java
@@ -25,6 +25,7 @@
 import android.provider.DocumentsContract;
 import android.provider.DocumentsContract.Document;
 import android.provider.DocumentsProvider;
+import android.support.annotation.VisibleForTesting;
 import android.text.TextUtils;
 
 import com.android.documentsui.DocumentsApplication;
@@ -204,13 +205,18 @@
         }
     }
 
-    private void deriveFields() {
+    @VisibleForTesting
+    void deriveFields() {
         derivedUri = DocumentsContract.buildDocumentUri(authority, documentId);
     }
 
     @Override
     public String toString() {
-        return "Document{docId=" + documentId + ", name=" + displayName + "}";
+        return "Document{"
+                + "docId=" + documentId
+                + ", name=" + displayName
+                + ", isDirectory=" + isDirectory()
+                + "}";
     }
 
     public boolean isCreateSupported() {
@@ -237,6 +243,22 @@
         return (flags & Document.FLAG_DIR_HIDE_GRID_TITLES) != 0;
     }
 
+    public int hashCode() {
+        return derivedUri.hashCode() + mimeType.hashCode();
+    }
+
+    public boolean equals(Object other) {
+        if (this == other) {
+            return true;
+        } else if (!(other instanceof DocumentInfo)) {
+            return false;
+        }
+
+        DocumentInfo that = (DocumentInfo) other;
+        // Uri + mime type should be totally unique.
+        return derivedUri.equals(that.derivedUri) && mimeType.equals(that.mimeType);
+    }
+
     public static String getCursorString(Cursor cursor, String columnName) {
         final int index = cursor.getColumnIndex(columnName);
         return (index != -1) ? cursor.getString(index) : null;
diff --git a/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java b/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java
index ae5644d..4caa891 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java
@@ -23,8 +23,10 @@
 import android.content.Context;
 import android.database.Cursor;
 import android.graphics.drawable.Drawable;
+import android.net.Uri;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.provider.DocumentsContract;
 import android.provider.DocumentsContract.Root;
 import android.text.TextUtils;
 
@@ -195,6 +197,10 @@
         }
     }
 
+    public Uri getUri() {
+        return DocumentsContract.buildRootUri(authority, rootId);
+    }
+
     public boolean isRecents() {
         return authority == null && rootId == null;
     }
@@ -238,11 +244,6 @@
                 || derivedType == TYPE_RECENTS || derivedType == TYPE_DOWNLOADS;
     }
 
-    @Override
-    public String toString() {
-        return "Root{authority=" + authority + ", rootId=" + rootId + ", title=" + title + "}";
-    }
-
     public Drawable loadIcon(Context context) {
         if (derivedIcon != 0) {
             return context.getDrawable(derivedIcon);
@@ -283,6 +284,11 @@
         return Objects.hash(authority, rootId);
     }
 
+    @Override
+    public String toString() {
+        return "Root{authority=" + authority + ", rootId=" + rootId + ", title=" + title + "}";
+    }
+
     public String getDirectoryString() {
         return !TextUtils.isEmpty(summary) ? summary : title;
     }
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/CopyTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/CopyServiceTest.java
similarity index 85%
rename from packages/DocumentsUI/tests/src/com/android/documentsui/CopyTest.java
rename to packages/DocumentsUI/tests/src/com/android/documentsui/CopyServiceTest.java
index 369ab7d..079d599 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/CopyTest.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/CopyServiceTest.java
@@ -28,12 +28,10 @@
 import android.os.Parcelable;
 import android.os.RemoteException;
 import android.provider.DocumentsContract;
-import android.provider.DocumentsContract.Document;
 import android.test.MoreAsserts;
 import android.test.ServiceTestCase;
 import android.test.mock.MockContentResolver;
 import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.util.Log;
 
 import com.android.documentsui.model.DocumentInfo;
@@ -48,6 +46,7 @@
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
+import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.CountDownLatch;
@@ -55,9 +54,9 @@
 import java.util.concurrent.TimeoutException;
 
 @MediumTest
-public class CopyTest extends ServiceTestCase<CopyService> {
+public class CopyServiceTest extends ServiceTestCase<CopyService> {
 
-    public CopyTest() {
+    public CopyServiceTest() {
         super(CopyService.class);
     }
 
@@ -72,11 +71,13 @@
     private DocumentsProviderHelper mDocHelper;
     private StubProvider mStorage;
     private Context mSystemContext;
+    private CopyJobListener mListener;
 
     @Override
     protected void setUp() throws Exception {
         super.setUp();
 
+        mListener = new CopyJobListener();
         setupTestContext();
         mClient = mResolver.acquireContentProviderClient(AUTHORITY);
 
@@ -84,6 +85,8 @@
         mStorage.clearCacheAndBuildRoots();
 
         mDocHelper = new DocumentsProviderHelper(AUTHORITY, mClient);
+
+        assertDestFileCount(0);
     }
 
     @Override
@@ -97,15 +100,13 @@
         Uri testFile = mStorage.createFile(SRC_ROOT, srcPath, "text/plain",
                 "The five boxing wizards jump quickly".getBytes());
 
-        assertDstFileCountEquals(0);
-
         startService(createCopyIntent(Lists.newArrayList(testFile)));
 
         // 2 operations: file creation, then writing data.
         mResolver.waitForChanges(2);
 
         // Verify that one file was copied; check file contents.
-        assertDstFileCountEquals(1);
+        assertDestFileCount(1);
         assertCopied(srcPath);
     }
 
@@ -114,8 +115,6 @@
         String testContent = "The five boxing wizards jump quickly";
         Uri testFile = mStorage.createFile(SRC_ROOT, srcPath, "text/plain", testContent.getBytes());
 
-        assertDstFileCountEquals(0);
-
         Intent moveIntent = createCopyIntent(Lists.newArrayList(testFile));
         moveIntent.putExtra(CopyService.EXTRA_TRANSFER_MODE, CopyService.TRANSFER_MODE_MOVE);
         startService(moveIntent);
@@ -124,7 +123,7 @@
         mResolver.waitForChanges(3);
 
         // Verify that one file was moved; check file contents.
-        assertDstFileCountEquals(1);
+        assertDestFileCount(1);
         assertDoesNotExist(SRC_ROOT, srcPath);
 
         byte[] dstContent = readFile(DST_ROOT, srcPath);
@@ -147,15 +146,13 @@
                 mStorage.createFile(SRC_ROOT, srcPaths[1], "text/plain", testContent[1].getBytes()),
                 mStorage.createFile(SRC_ROOT, srcPaths[2], "text/plain", testContent[2].getBytes()));
 
-        assertDstFileCountEquals(0);
-
         // Copy all the test files.
         startService(createCopyIntent(testFiles));
 
         // 3 file creations, 3 file writes.
         mResolver.waitForChanges(6);
 
-        assertDstFileCountEquals(3);
+        assertDestFileCount(3);
         for (String path : srcPaths) {
             assertCopied(path);
         }
@@ -163,29 +160,54 @@
 
     public void testCopyEmptyDir() throws Exception {
         String srcPath = "/emptyDir";
-        Uri testDir = mStorage.createFile(SRC_ROOT, srcPath, DocumentsContract.Document.MIME_TYPE_DIR,
-                null);
-
-        assertDstFileCountEquals(0);
+        Uri testDir = createTestDirectory(srcPath);
 
         startService(createCopyIntent(Lists.newArrayList(testDir)));
 
         // Just 1 operation: Directory creation.
         mResolver.waitForChanges(1);
 
-        assertDstFileCountEquals(1);
+        assertDestFileCount(1);
 
         // Verify that the dst exists and is a directory.
         File dst = mStorage.getFile(DST_ROOT, srcPath);
         assertTrue(dst.isDirectory());
     }
 
+    public void testNoCopyDirToSelf() throws Exception {
+        Uri testDir = createTestDirectory("/someDir");
+
+        Intent intent = createCopyIntent(Lists.newArrayList(testDir), testDir);
+        startService(intent);
+
+        getService().addFinishedListener(mListener);
+
+        mListener.waitForFinished();
+        mListener.assertFailedCount(1);
+        mListener.assertFileFailed("someDir");
+
+        assertDestFileCount(0);
+    }
+
+    public void testNoCopyDirToDescendent() throws Exception {
+        Uri testDir = createTestDirectory("/someDir");
+        Uri descDir = createTestDirectory("/someDir/theDescendent");
+
+        Intent intent = createCopyIntent(Lists.newArrayList(testDir), descDir);
+        startService(intent);
+
+        getService().addFinishedListener(mListener);
+
+        mListener.waitForFinished();
+        mListener.assertFailedCount(1);
+        mListener.assertFileFailed("someDir");
+
+        assertDestFileCount(0);
+    }
+
     public void testMoveEmptyDir() throws Exception {
         String srcPath = "/emptyDir";
-        Uri testDir = mStorage.createFile(SRC_ROOT, srcPath, DocumentsContract.Document.MIME_TYPE_DIR,
-                null);
-
-        assertDstFileCountEquals(0);
+        Uri testDir = createTestDirectory(srcPath);
 
         Intent moveIntent = createCopyIntent(Lists.newArrayList(testDir));
         moveIntent.putExtra(CopyService.EXTRA_TRANSFER_MODE, CopyService.TRANSFER_MODE_MOVE);
@@ -194,7 +216,7 @@
         // 2 operations: Directory creation, and removal of the original.
         mResolver.waitForChanges(2);
 
-        assertDstFileCountEquals(1);
+        assertDestFileCount(1);
 
         // Verify that the dst exists and is a directory.
         File dst = mStorage.getFile(DST_ROOT, srcPath);
@@ -217,8 +239,7 @@
                 srcDir + "/test2.txt"
         };
         // Create test dir; put some files in it.
-        Uri testDir = mStorage.createFile(SRC_ROOT, srcDir, DocumentsContract.Document.MIME_TYPE_DIR,
-                null);
+        Uri testDir = createTestDirectory(srcDir);
         mStorage.createFile(SRC_ROOT, srcFiles[0], "text/plain", testContent[0].getBytes());
         mStorage.createFile(SRC_ROOT, srcFiles[1], "text/plain", testContent[1].getBytes());
         mStorage.createFile(SRC_ROOT, srcFiles[2], "text/plain", testContent[2].getBytes());
@@ -252,8 +273,6 @@
         Uri testFile = mStorage.createFile(SRC_ROOT, srcPath, "text/plain",
                 "The five boxing wizards jump quickly".getBytes());
 
-        assertDstFileCountEquals(0);
-
         mStorage.simulateReadErrorsForFile(testFile);
 
         startService(createCopyIntent(Lists.newArrayList(testFile)));
@@ -262,7 +281,7 @@
         mResolver.waitForChanges(3);
 
         // Verify that the failed copy was cleaned up.
-        assertDstFileCountEquals(0);
+        assertDestFileCount(0);
     }
 
     public void testMoveFileWithReadErrors() throws Exception {
@@ -270,8 +289,6 @@
         Uri testFile = mStorage.createFile(SRC_ROOT, srcPath, "text/plain",
                 "The five boxing wizards jump quickly".getBytes());
 
-        assertDstFileCountEquals(0);
-
         mStorage.simulateReadErrorsForFile(testFile);
 
         Intent moveIntent = createCopyIntent(Lists.newArrayList(testFile));
@@ -288,7 +305,7 @@
             return;
         } finally {
             // Verify that the failed copy was cleaned up, and the src file wasn't removed.
-            assertDstFileCountEquals(0);
+            assertDestFileCount(0);
             assertExists(SRC_ROOT, srcPath);
         }
         // The asserts above didn't fail, but the CopyService did something unexpected.
@@ -308,8 +325,7 @@
                 srcDir + "/test2.txt"
         };
         // Create test dir; put some files in it.
-        Uri testDir = mStorage.createFile(SRC_ROOT, srcDir, DocumentsContract.Document.MIME_TYPE_DIR,
-                null);
+        Uri testDir = createTestDirectory(srcDir);
         mStorage.createFile(SRC_ROOT, srcFiles[0], "text/plain", testContent[0].getBytes());
         Uri errFile = mStorage
                 .createFile(SRC_ROOT, srcFiles[1], "text/plain", testContent[1].getBytes());
@@ -346,33 +362,37 @@
         assertExists(SRC_ROOT, srcFiles[1]);
     }
 
-    /**
-     * Copies the given files to a pre-determined destination.
-     *
-     * @throws FileNotFoundException
-     */
+    private Uri createTestDirectory(String dir) throws IOException {
+        return mStorage.createFile(
+                SRC_ROOT, dir, DocumentsContract.Document.MIME_TYPE_DIR, null);
+    }
+
     private Intent createCopyIntent(List<Uri> srcs) throws Exception {
+        RootInfo root = mDocHelper.getRoot(DST_ROOT);
+        final Uri dst = DocumentsContract.buildDocumentUri(AUTHORITY, root.documentId);
+
+        return createCopyIntent(srcs, dst);
+    }
+
+    private Intent createCopyIntent(List<Uri> srcs, Uri dst) throws Exception {
         final ArrayList<DocumentInfo> srcDocs = Lists.newArrayList();
         for (Uri src : srcs) {
             srcDocs.add(DocumentInfo.fromUri(mResolver, src));
         }
 
-        RootInfo root = mDocHelper.getRoot(DST_ROOT);
-        final Uri dst = DocumentsContract.buildDocumentUri(AUTHORITY, root.documentId);
         DocumentStack stack = new DocumentStack();
         stack.push(DocumentInfo.fromUri(mResolver, dst));
         final Intent copyIntent = new Intent(mContext, CopyService.class);
         copyIntent.putParcelableArrayListExtra(CopyService.EXTRA_SRC_LIST, srcDocs);
         copyIntent.putExtra(Shared.EXTRA_STACK, (Parcelable) stack);
 
-        // startService(copyIntent);
         return copyIntent;
     }
 
     /**
      * Returns a count of the files in the given directory.
      */
-    private void assertDstFileCountEquals(int expected) throws RemoteException {
+    private void assertDestFileCount(int expected) throws RemoteException {
         RootInfo dest = mDocHelper.getRoot(DST_ROOT);
         final Uri queryUri = DocumentsContract.buildChildDocumentsUri(AUTHORITY,
                 dest.documentId);
@@ -449,6 +469,34 @@
         mResolver.addProvider(AUTHORITY, mStorage);
     }
 
+    private final class CopyJobListener implements CopyService.TestOnlyListener {
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        final List<DocumentInfo> failedDocs = new ArrayList<>();
+        @Override
+        public void onFinished(List<DocumentInfo> failed) {
+            failedDocs.addAll(failed);
+            latch.countDown();
+        }
+
+        public void assertFileFailed(String expectedName) {
+            for (DocumentInfo failed : failedDocs) {
+                if (expectedName.equals(failed.displayName)) {
+                    return;
+                }
+            }
+            fail("Couldn't find failed file: " + expectedName);
+        }
+
+        public void waitForFinished() throws InterruptedException {
+            latch.await(500, TimeUnit.MILLISECONDS);
+        }
+
+        public void assertFailedCount(int expected) {
+            assertEquals(expected, failedDocs.size());
+        }
+    }
+
     /**
      * A test resolver that enables this test suite to listen for notifications that mark when copy
      * operations are done.
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/DownloadsActivityUiTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/DownloadsActivityUiTest.java
new file mode 100644
index 0000000..737a8b6
--- /dev/null
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/DownloadsActivityUiTest.java
@@ -0,0 +1,154 @@
+/*
+ * 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.documentsui;
+
+import static com.android.documentsui.StubProvider.DEFAULT_AUTHORITY;
+import static com.android.documentsui.StubProvider.ROOT_0_ID;
+
+import android.app.Instrumentation;
+import android.content.ContentProviderClient;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.RemoteException;
+import android.provider.DocumentsContract;
+import android.support.test.uiautomator.By;
+import android.support.test.uiautomator.Configurator;
+import android.support.test.uiautomator.UiDevice;
+import android.support.test.uiautomator.Until;
+import android.test.InstrumentationTestCase;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.util.Log;
+import android.view.MotionEvent;
+
+import com.android.documentsui.model.RootInfo;
+
+@LargeTest
+public class DownloadsActivityUiTest extends InstrumentationTestCase {
+
+    private static final int TIMEOUT = 5000;
+    private static final String TAG = "DownloadsActivityUiTest";
+    private static final String TARGET_PKG = "com.android.documentsui";
+    private static final String LAUNCHER_PKG = "com.android.launcher";
+
+    private UiBot mBot;
+    private UiDevice mDevice;
+    private Context mContext;
+    private ContentResolver mResolver;
+    private DocumentsProviderHelper mDocsHelper;
+    private ContentProviderClient mClient;
+    private RootInfo mRoot;
+
+    public void setUp() throws Exception {
+        // Initialize UiDevice instance.
+        Instrumentation instrumentation = getInstrumentation();
+
+        mDevice = UiDevice.getInstance(instrumentation);
+
+        Configurator.getInstance().setToolType(MotionEvent.TOOL_TYPE_MOUSE);
+
+        // Start from the home screen.
+        mDevice.pressHome();
+        mDevice.wait(Until.hasObject(By.pkg(LAUNCHER_PKG).depth(0)), TIMEOUT);
+
+        // NOTE: Must be the "target" context, else security checks in content provider will fail.
+        mContext = instrumentation.getTargetContext();
+        mResolver = mContext.getContentResolver();
+
+        mClient = mResolver.acquireUnstableContentProviderClient(DEFAULT_AUTHORITY);
+        mDocsHelper = new DocumentsProviderHelper(DEFAULT_AUTHORITY, mClient);
+
+        mRoot = mDocsHelper.getRoot(ROOT_0_ID);
+
+        // Open the Downloads activity on our stub provider root.
+        Intent intent = new Intent(DocumentsContract.ACTION_MANAGE_ROOT);
+        intent.setDataAndType(mRoot.getUri(), DocumentsContract.Root.MIME_TYPE_ITEM);
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
+        mContext.startActivity(intent);
+
+        // Wait for the app to appear.
+        mDevice.wait(Until.hasObject(By.pkg(TARGET_PKG).depth(0)), TIMEOUT);
+        mDevice.waitForIdle();
+
+        mBot = new UiBot(mDevice, TIMEOUT);
+
+        resetStorage();  // Just in case a test failed and tearDown didn't happen.
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        // Need to kill off the task we started.
+        super.tearDown();
+        Log.d(TAG, "Resetting storage from setUp");
+        resetStorage();
+        mClient.release();
+    }
+
+    private void resetStorage() throws RemoteException {
+        mClient.call("clear", null, null);
+        // TODO: Would be nice to have an event to wait on here.
+        mDevice.waitForIdle();
+    }
+
+    private void initTestFiles() throws RemoteException {
+        mDocsHelper.createDocument(mRoot, "text/plain", "file0.log");
+        mDocsHelper.createDocument(mRoot, "image/png", "file1.png");
+        mDocsHelper.createDocument(mRoot, "text/csv", "file2.csv");
+    }
+
+    public void testWindowTitle() throws Exception {
+        initTestFiles();
+
+        mBot.assertWindowTitle(ROOT_0_ID);
+    }
+
+    public void testFilesListed() throws Exception {
+        initTestFiles();
+
+        mBot.assertHasDocuments("file0.log", "file1.png", "file2.csv");
+    }
+
+    public void testFilesList_LiveUpdate() throws Exception {
+        initTestFiles();
+
+        mDocsHelper.createDocument(mRoot, "yummers/sandwich", "Ham & Cheese.sandwich");
+        mBot.assertHasDocuments("file0.log", "file1.png", "file2.csv", "Ham & Cheese.sandwich");
+    }
+
+    public void testDeleteDocument() throws Exception {
+        initTestFiles();
+
+        mBot.clickDocument("file1.png");
+        mDevice.waitForIdle();
+        mBot.menuDelete().click();
+
+        mBot.waitForDeleteSnackbar();
+        assertFalse(mBot.hasDocuments("file1.png"));
+
+        mBot.waitForDeleteSnackbarGone();
+        assertFalse(mBot.hasDocuments("file1.png"));
+    }
+
+    public void testSupportsShare() throws Exception {
+        initTestFiles();
+
+        mBot.clickDocument("file1.png");
+        mDevice.waitForIdle();
+        assertNotNull(mBot.menuShare());
+    }
+}
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/StubProvider.java b/packages/DocumentsUI/tests/src/com/android/documentsui/StubProvider.java
index 2d42ddc..7a75503 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/StubProvider.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/StubProvider.java
@@ -226,6 +226,12 @@
     }
 
     @Override
+    public Cursor queryChildDocumentsForManage(String parentDocumentId, String[] projection,
+            String sortOrder) throws FileNotFoundException {
+        return queryChildDocuments(parentDocumentId, projection, sortOrder);
+    }
+
+    @Override
     public Cursor queryChildDocuments(String parentDocumentId, String[] projection, String sortOrder)
             throws FileNotFoundException {
         final StubDocument parentDocument = mStorage.get(parentDocumentId);
@@ -531,6 +537,16 @@
             this.rootInfo = rootInfo;
             mStorage.put(this.documentId, this);
         }
+        @Override
+        public String toString() {
+            return "StubDocument{"
+                    + "path:" + file.getPath()
+                    + ", mimeType:" + mimeType
+                    + ", rootInfo:" + rootInfo
+                    + ", documentId:" + documentId
+                    + ", parentId:" + parentId
+                    + "}";
+        }
     }
 
     private static String getDocumentIdForFile(File file) {
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/UiBot.java b/packages/DocumentsUI/tests/src/com/android/documentsui/UiBot.java
index ecad061..68cdf12 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/UiBot.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/UiBot.java
@@ -179,6 +179,10 @@
         return find(By.res("com.android.documentsui:id/menu_delete"));
     }
 
+    UiObject2 menuShare() {
+        return find(By.res("com.android.documentsui:id/menu_share"));
+    }
+
     private UiObject2 find(BySelector selector) {
         mDevice.wait(Until.findObject(selector), mTimeout);
         return mDevice.findObject(selector);
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/model/DocumentInfoTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/model/DocumentInfoTest.java
new file mode 100644
index 0000000..a6aba7b
--- /dev/null
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/model/DocumentInfoTest.java
@@ -0,0 +1,56 @@
+/*
+ * 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.documentsui.model;
+
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+@SmallTest
+public class DocumentInfoTest extends AndroidTestCase {
+
+    public void testEquals() throws Exception {
+        DocumentInfo doc = createDocInfo("authority.a", "doc.1", "text/plain");
+        assertEquals(doc, doc);
+    }
+
+    public void testNotEquals_differentAuthority() throws Exception {
+        DocumentInfo docA = createDocInfo("authority.a", "doc.1", "text/plain");
+        DocumentInfo docB = createDocInfo("authority.b", "doc.1", "text/plain");
+        assertFalse(docA.equals(docB));
+    }
+
+    public void testNotEquals_differentDocId() throws Exception {
+        DocumentInfo docA = createDocInfo("authority.a", "doc.1", "text/plain");
+        DocumentInfo docB = createDocInfo("authority.a", "doc.2", "text/plain");
+        assertFalse(docA.equals(docB));
+    }
+
+    public void testNotEquals_differentMimetype() throws Exception {
+        DocumentInfo docA = createDocInfo("authority.a", "doc.1", "text/plain");
+        DocumentInfo docB = createDocInfo("authority.a", "doc.1", "image/png");
+        assertFalse(docA.equals(docB));
+    }
+
+    private DocumentInfo createDocInfo(String authority, String docId, String mimeType) {
+        DocumentInfo doc = new DocumentInfo();
+        doc.authority = authority;
+        doc.documentId = docId;
+        doc.mimeType = mimeType;
+        doc.deriveFields();
+        return doc;
+    }
+}
diff --git a/packages/Keyguard/res/values/config.xml b/packages/Keyguard/res/values/config.xml
index b398ab2..bde6ed5 100644
--- a/packages/Keyguard/res/values/config.xml
+++ b/packages/Keyguard/res/values/config.xml
@@ -23,9 +23,9 @@
     <!-- Allow the menu hard key to be disabled in LockScreen on some devices [DO NOT TRANSLATE] -->
     <bool name="config_disableMenuKeyInLockScreen">false</bool>
 
-    <!-- Threshold in micro amperes below which a charger is rated as "slow" -->
-    <integer name="config_chargingSlowlyThreshold">1000000</integer>
+    <!-- Threshold in micro watts below which a charger is rated as "slow"; 1A @ 5V -->
+    <integer name="config_chargingSlowlyThreshold">5000000</integer>
 
-    <!-- Threshold in micro amperes above which a charger is rated as "fast" -->
-    <integer name="config_chargingFastThreshold">1500000</integer>
+    <!-- Threshold in micro watts above which a charger is rated as "fast"; 1.5A @ 5V  -->
+    <integer name="config_chargingFastThreshold">7500000</integer>
 </resources>
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 3d78028..8102c34 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -75,6 +75,7 @@
 import static android.os.BatteryManager.EXTRA_HEALTH;
 import static android.os.BatteryManager.EXTRA_LEVEL;
 import static android.os.BatteryManager.EXTRA_MAX_CHARGING_CURRENT;
+import static android.os.BatteryManager.EXTRA_MAX_CHARGING_VOLTAGE;
 import static android.os.BatteryManager.EXTRA_PLUGGED;
 import static android.os.BatteryManager.EXTRA_STATUS;
 
@@ -155,6 +156,8 @@
      */
     private static final int FINGERPRINT_STATE_CANCELLING_RESTARTING = 3;
 
+    private static final int DEFAULT_CHARGING_VOLTAGE_MICRO_VOLT = 5000000;
+
     private static KeyguardUpdateMonitor sInstance;
 
     private final Context mContext;
@@ -617,10 +620,25 @@
                 final int plugged = intent.getIntExtra(EXTRA_PLUGGED, 0);
                 final int level = intent.getIntExtra(EXTRA_LEVEL, 0);
                 final int health = intent.getIntExtra(EXTRA_HEALTH, BATTERY_HEALTH_UNKNOWN);
-                final int maxChargingCurrent = intent.getIntExtra(EXTRA_MAX_CHARGING_CURRENT, -1);
+
+                final int maxChargingMicroAmp = intent.getIntExtra(EXTRA_MAX_CHARGING_CURRENT, -1);
+                int maxChargingMicroVolt = intent.getIntExtra(EXTRA_MAX_CHARGING_VOLTAGE, -1);
+                final int maxChargingMicroWatt;
+
+                if (maxChargingMicroVolt <= 0) {
+                    maxChargingMicroVolt = DEFAULT_CHARGING_VOLTAGE_MICRO_VOLT;
+                }
+                if (maxChargingMicroAmp > 0) {
+                    // Calculating muW = muA * muV / (10^6 mu^2 / mu); splitting up the divisor
+                    // to maintain precision equally on both factors.
+                    maxChargingMicroWatt = (maxChargingMicroAmp / 1000)
+                            * (maxChargingMicroVolt / 1000);
+                } else {
+                    maxChargingMicroWatt = -1;
+                }
                 final Message msg = mHandler.obtainMessage(
                         MSG_BATTERY_UPDATE, new BatteryStatus(status, level, plugged, health,
-                        maxChargingCurrent));
+                                maxChargingMicroWatt));
                 mHandler.sendMessage(msg);
             } else if (TelephonyIntents.ACTION_SIM_STATE_CHANGED.equals(action)) {
                 SimData args = SimData.fromIntent(intent);
@@ -806,13 +824,14 @@
         public final int level;
         public final int plugged;
         public final int health;
-        public final int maxChargingCurrent;
-        public BatteryStatus(int status, int level, int plugged, int health, int maxChargingCurrent) {
+        public final int maxChargingWattage;
+        public BatteryStatus(int status, int level, int plugged, int health,
+                int maxChargingWattage) {
             this.status = status;
             this.level = level;
             this.plugged = plugged;
             this.health = health;
-            this.maxChargingCurrent = maxChargingCurrent;
+            this.maxChargingWattage = maxChargingWattage;
         }
 
         /**
@@ -844,9 +863,9 @@
         }
 
         public final int getChargingSpeed(int slowThreshold, int fastThreshold) {
-            return maxChargingCurrent <= 0 ? CHARGING_UNKNOWN :
-                    maxChargingCurrent < slowThreshold ? CHARGING_SLOWLY :
-                    maxChargingCurrent > fastThreshold ? CHARGING_FAST :
+            return maxChargingWattage <= 0 ? CHARGING_UNKNOWN :
+                    maxChargingWattage < slowThreshold ? CHARGING_SLOWLY :
+                    maxChargingWattage > fastThreshold ? CHARGING_FAST :
                     CHARGING_REGULAR;
         }
     }
@@ -1422,7 +1441,7 @@
         }
 
         // change in charging current while plugged in
-        if (nowPluggedIn && current.maxChargingCurrent != old.maxChargingCurrent) {
+        if (nowPluggedIn && current.maxChargingWattage != old.maxChargingWattage) {
             return true;
         }
 
diff --git a/packages/MtpDocumentsProvider/res/drawable-hdpi/ic_root_mtp.png b/packages/MtpDocumentsProvider/res/drawable-hdpi/ic_root_mtp.png
new file mode 100644
index 0000000..7691433
--- /dev/null
+++ b/packages/MtpDocumentsProvider/res/drawable-hdpi/ic_root_mtp.png
Binary files differ
diff --git a/packages/MtpDocumentsProvider/res/drawable-mdpi/ic_root_mtp.png b/packages/MtpDocumentsProvider/res/drawable-mdpi/ic_root_mtp.png
new file mode 100644
index 0000000..1cf7b3a
--- /dev/null
+++ b/packages/MtpDocumentsProvider/res/drawable-mdpi/ic_root_mtp.png
Binary files differ
diff --git a/packages/MtpDocumentsProvider/res/drawable-xhdpi/ic_root_mtp.png b/packages/MtpDocumentsProvider/res/drawable-xhdpi/ic_root_mtp.png
new file mode 100644
index 0000000..27e3542
--- /dev/null
+++ b/packages/MtpDocumentsProvider/res/drawable-xhdpi/ic_root_mtp.png
Binary files differ
diff --git a/packages/MtpDocumentsProvider/res/drawable-xxhdpi/ic_root_mtp.png b/packages/MtpDocumentsProvider/res/drawable-xxhdpi/ic_root_mtp.png
new file mode 100644
index 0000000..3df2578b
--- /dev/null
+++ b/packages/MtpDocumentsProvider/res/drawable-xxhdpi/ic_root_mtp.png
Binary files differ
diff --git a/packages/MtpDocumentsProvider/res/drawable-xxxhdpi/ic_root_mtp.png b/packages/MtpDocumentsProvider/res/drawable-xxxhdpi/ic_root_mtp.png
new file mode 100644
index 0000000..fd2b795
--- /dev/null
+++ b/packages/MtpDocumentsProvider/res/drawable-xxxhdpi/ic_root_mtp.png
Binary files differ
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java
index 00fe7a7..51c0281 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java
@@ -401,7 +401,7 @@
         values.put(Document.COLUMN_DISPLAY_NAME, root.getRootName(resources));
         values.putNull(Document.COLUMN_SUMMARY);
         values.putNull(Document.COLUMN_LAST_MODIFIED);
-        values.putNull(Document.COLUMN_ICON);
+        values.put(Document.COLUMN_ICON, R.drawable.ic_root_mtp);
         values.put(Document.COLUMN_FLAGS, 0);
         values.put(Document.COLUMN_SIZE,
                 (int) Math.min(root.mMaxCapacity - root.mFreeSpace, Integer.MAX_VALUE));
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java
index 6d9193d..67b0672 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java
@@ -80,7 +80,7 @@
             assertEquals("displayName", "Device Storage", cursor.getString(5));
             assertTrue("summary", cursor.isNull(6));
             assertTrue("lastModified", cursor.isNull(7));
-            assertTrue("icon", cursor.isNull(8));
+            assertEquals("icon", R.drawable.ic_root_mtp, cursor.getInt(8));
             assertEquals("flag", 0, cursor.getInt(9));
             assertEquals("size", 1000, cursor.getInt(10));
 
@@ -111,7 +111,7 @@
             cursor.moveToNext();
             assertEquals(1, cursor.getInt(0));
             assertEquals(Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE, cursor.getInt(1));
-            assertTrue(cursor.isNull(2));
+            assertEquals(R.drawable.ic_root_mtp, cursor.getInt(2));
             assertEquals("Device Storage", cursor.getString(3));
             assertTrue(cursor.isNull(4));
             assertEquals(1, cursor.getInt(5));
@@ -121,7 +121,7 @@
             cursor.moveToNext();
             assertEquals(2, cursor.getInt(0));
             assertEquals(Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE, cursor.getInt(1));
-            assertTrue(cursor.isNull(2));
+            assertEquals(R.drawable.ic_root_mtp, cursor.getInt(2));
             assertEquals("Device Storage", cursor.getString(3));
             assertTrue(cursor.isNull(4));
             assertEquals(2, cursor.getInt(5));
@@ -131,7 +131,7 @@
             cursor.moveToNext();
             assertEquals(3, cursor.getInt(0));
             assertEquals(Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE, cursor.getInt(1));
-            assertTrue(cursor.isNull(2));
+            assertEquals(R.drawable.ic_root_mtp, cursor.getInt(2));
             assertEquals("Device /@#%&<>Storage", cursor.getString(3));
             assertTrue(cursor.isNull(4));
             assertEquals(3, cursor.getInt(5));
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
index b20b3bb..dc6f79e 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
@@ -140,8 +140,7 @@
             cursor.moveToNext();
             assertEquals("1", cursor.getString(0));
             assertEquals(Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE, cursor.getInt(1));
-            // TODO: Add storage icon for MTP devices.
-            assertTrue(cursor.isNull(2) /* icon */);
+            assertEquals(R.drawable.ic_root_mtp, cursor.getInt(2));
             assertEquals("Device A Storage A", cursor.getString(3));
             assertEquals("1", cursor.getString(4));
             assertEquals(1024, cursor.getInt(5));
@@ -156,8 +155,7 @@
             cursor.moveToNext();
             assertEquals("2", cursor.getString(0));
             assertEquals(Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE, cursor.getInt(1));
-            // TODO: Add storage icon for MTP devices.
-            assertTrue(cursor.isNull(2) /* icon */);
+            assertEquals(R.drawable.ic_root_mtp, cursor.getInt(2));
             assertEquals("Device B Storage B", cursor.getString(3));
             assertEquals("2", cursor.getString(4));
             assertEquals(2048, cursor.getInt(5));
@@ -189,8 +187,7 @@
             cursor.moveToNext();
             assertEquals("1", cursor.getString(0));
             assertEquals(Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE, cursor.getInt(1));
-            // TODO: Add storage icon for MTP devices.
-            assertTrue(cursor.isNull(2) /* icon */);
+            assertEquals(R.drawable.ic_root_mtp, cursor.getInt(2));
             assertEquals("Device B Storage B", cursor.getString(3));
             assertEquals("1", cursor.getString(4));
             assertEquals(2048, cursor.getInt(5));
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpManagerTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpManagerTest.java
index ed6ee7e..a045d06 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpManagerTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpManagerTest.java
@@ -16,26 +16,18 @@
 
 package com.android.mtp;
 
-import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
 import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
 import android.hardware.usb.UsbDevice;
-import android.hardware.usb.UsbDeviceConnection;
 import android.hardware.usb.UsbManager;
 import android.os.CancellationSignal;
 import android.os.OperationCanceledException;
 import android.test.InstrumentationTestCase;
 
 import java.io.IOException;
-import java.util.HashMap;
-import java.util.concurrent.CountDownLatch;
 
 @RealDeviceTest
 public class MtpManagerTest extends InstrumentationTestCase {
-    private static final String ACTION_USB_PERMISSION =
-            "com.android.mtp.USB_PERMISSION";
+
     private static final int TIMEOUT_MS = 1000;
     UsbManager mUsbManager;
     MtpManager mManager;
@@ -45,20 +37,8 @@
     @Override
     public void setUp() throws Exception {
         mUsbManager = getContext().getSystemService(UsbManager.class);
-        for (int i = 0; i < 2; i++) {
-            mUsbDevice = findDevice();
-            mManager = new MtpManager(getContext());
-            mManager.openDevice(mUsbDevice.getDeviceId());
-            try {
-                waitForStorages(mManager, mUsbDevice.getDeviceId());
-                return;
-            } catch (IOException exp) {
-                // When the MTP device is Android, and it changes the USB device type from
-                // "Charging" to "MTP", the device ID will be updated. We need to find a device
-                // again.
-                continue;
-            }
-        }
+        mManager = new MtpManager(getContext());
+        mUsbDevice = TestUtil.setupMtpDevice(getInstrumentation(), mUsbManager, mManager);
     }
 
     @Override
@@ -66,6 +46,11 @@
         mManager.closeDevice(mUsbDevice.getDeviceId());
     }
 
+    @Override
+    public TestResultInstrumentation getInstrumentation() {
+        return (TestResultInstrumentation) super.getInstrumentation();
+    }
+
     public void testCancelEvent() throws Exception {
         final CancellationSignal signal = new CancellationSignal();
         final Thread thread = new Thread() {
@@ -74,7 +59,7 @@
                 try {
                     mManager.readEvent(mUsbDevice.getDeviceId(), signal);
                 } catch (OperationCanceledException | IOException e) {
-                    show(e.getMessage());
+                    getInstrumentation().show(e.getMessage());
                 }
             }
         };
@@ -84,72 +69,6 @@
         thread.join(TIMEOUT_MS);
     }
 
-    private void requestPermission(UsbDevice device) throws InterruptedException {
-        if (mUsbManager.hasPermission(device)) {
-            return;
-        }
-        final CountDownLatch latch = new CountDownLatch(1);
-        final BroadcastReceiver receiver = new BroadcastReceiver() {
-            @Override
-            public void onReceive(Context context, Intent intent) {
-                latch.countDown();
-                getInstrumentation().getTargetContext().unregisterReceiver(this);
-            }
-        };
-        getInstrumentation().getTargetContext().registerReceiver(
-                receiver, new IntentFilter(ACTION_USB_PERMISSION));
-        mUsbManager.requestPermission(device, PendingIntent.getBroadcast(
-                getInstrumentation().getTargetContext(),
-                0 /* requstCode */,
-                new Intent(ACTION_USB_PERMISSION),
-                0 /* flags */));
-        latch.await();
-        assertTrue(mUsbManager.hasPermission(device));
-    }
-
-    private UsbDevice findDevice() throws InterruptedException {
-        while (true) {
-            final HashMap<String,UsbDevice> devices = mUsbManager.getDeviceList();
-            if (devices.size() == 0) {
-                show("Wait for devices.");
-                Thread.sleep(1000);
-                continue;
-            }
-            final UsbDevice device = devices.values().iterator().next();
-            requestPermission(device);
-            final UsbDeviceConnection connection = mUsbManager.openDevice(device);
-            if (connection == null) {
-                fail("Cannot open USB connection.");
-            }
-            for (int i = 0; i < device.getInterfaceCount(); i++) {
-                // Since the test runs real environment, we need to call claim interface with
-                // force = true to rob interfaces from other applications.
-                connection.claimInterface(device.getInterface(i), true);
-                connection.releaseInterface(device.getInterface(i));
-            }
-            connection.close();
-            return device;
-        }
-    }
-
-    private void waitForStorages(MtpManager manager, int deviceId) throws Exception {
-        while (true) {
-            if (manager.getRoots(deviceId).length == 0) {
-                show("Wait for storages.");
-                Thread.sleep(1000);
-                continue;
-            }
-            return;
-        }
-    }
-
-    private void show(String message) {
-        if (!(getInstrumentation() instanceof TestResultInstrumentation)) {
-            return;
-        }
-        ((TestResultInstrumentation) getInstrumentation()).show(message);
-    }
-
     private Context getContext() {
         return getInstrumentation().getContext();
     }
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestResultInstrumentation.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestResultInstrumentation.java
index 0fb0f34..3e64f9a2 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestResultInstrumentation.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestResultInstrumentation.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package com.android.mtp;
 
 import android.os.Bundle;
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestUtil.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestUtil.java
new file mode 100644
index 0000000..e6c12cb
--- /dev/null
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestUtil.java
@@ -0,0 +1,134 @@
+/*
+ * 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.mtp;
+
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.hardware.usb.UsbDevice;
+import android.hardware.usb.UsbDeviceConnection;
+import android.hardware.usb.UsbManager;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.concurrent.CountDownLatch;
+import junit.framework.Assert;
+
+/**
+ * Static utility methods for testing.
+ */
+class TestUtil {
+    private static final String ACTION_USB_PERMISSION =
+            "com.android.mtp.USB_PERMISSION";
+
+    private TestUtil() {}
+
+    /**
+     * Requests permission for a MTP device and returns the first MTP device that has at least one
+     * storage.
+     * @throws Exception
+     */
+    static UsbDevice setupMtpDevice(
+            TestResultInstrumentation instrumentation,
+            UsbManager usbManager,
+            MtpManager manager) throws Exception {
+        for (int i = 0; i < 2; i++) {
+            final UsbDevice device = findMtpDevice(instrumentation, usbManager);
+            manager.openDevice(device.getDeviceId());
+            try {
+                waitForStorages(instrumentation, manager, device.getDeviceId());
+                return device;
+            } catch (IOException exp) {
+                // When the MTP device is Android, and it changes the USB device type from
+                // "Charging" to "MTP", the device ID will be updated. We need to find a device
+                // again.
+                continue;
+            }
+        }
+        throw new IOException("Failed to obtain MTP devices");
+    }
+
+    private static UsbDevice findMtpDevice(
+            TestResultInstrumentation instrumentation,
+            UsbManager usbManager) throws InterruptedException {
+        while (true) {
+            final HashMap<String,UsbDevice> devices = usbManager.getDeviceList();
+            if (devices.size() == 0) {
+                instrumentation.show("Wait for devices.");
+                Thread.sleep(1000);
+                continue;
+            }
+            final UsbDevice device = devices.values().iterator().next();
+            requestPermission(instrumentation, usbManager, device);
+            final UsbDeviceConnection connection = usbManager.openDevice(device);
+            if (connection == null) {
+                Assert.fail("Cannot open USB connection.");
+                return null;
+            }
+            for (int i = 0; i < device.getInterfaceCount(); i++) {
+                // Since the test runs real environment, we need to call claim interface with
+                // force = true to rob interfaces from other applications.
+                connection.claimInterface(device.getInterface(i), true);
+                connection.releaseInterface(device.getInterface(i));
+            }
+            connection.close();
+            return device;
+        }
+    }
+
+    private static void requestPermission(
+            final TestResultInstrumentation instrumentation,
+            UsbManager usbManager,
+            UsbDevice device) throws InterruptedException {
+        if (usbManager.hasPermission(device)) {
+            return;
+        }
+        final CountDownLatch latch = new CountDownLatch(1);
+        final BroadcastReceiver receiver = new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                latch.countDown();
+                instrumentation.getTargetContext().unregisterReceiver(this);
+            }
+        };
+        instrumentation.getTargetContext().registerReceiver(
+                receiver, new IntentFilter(ACTION_USB_PERMISSION));
+        usbManager.requestPermission(device, PendingIntent.getBroadcast(
+                instrumentation.getTargetContext(),
+                0 /* requstCode */,
+                new Intent(ACTION_USB_PERMISSION),
+                0 /* flags */));
+        latch.await();
+        Assert.assertTrue(usbManager.hasPermission(device));
+    }
+
+    private static void waitForStorages(
+            TestResultInstrumentation instrumentation,
+            MtpManager manager,
+            int deviceId) throws Exception {
+        while (true) {
+            if (manager.getRoots(deviceId).length == 0) {
+                instrumentation.show("Wait for storages.");
+                Thread.sleep(1000);
+                continue;
+            }
+            return;
+        }
+    }
+}
diff --git a/packages/PrintSpooler/src/com/android/printspooler/model/NotificationController.java b/packages/PrintSpooler/src/com/android/printspooler/model/NotificationController.java
index f006ccb..82fd512 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/model/NotificationController.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/model/NotificationController.java
@@ -16,6 +16,8 @@
 
 package com.android.printspooler.model;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.Notification;
 import android.app.Notification.Action;
 import android.app.Notification.InboxStyle;
@@ -129,68 +131,69 @@
                 mContext.getString(R.string.cancel), createCancelIntent(printJob)).build();
     }
 
-    private void createPrintingNotification(PrintJobInfo printJob) {
+    /**
+     * Create a notification for a print job.
+     *
+     * @param printJob the job the notification is for
+     * @param firstAction the first action shown in the notification
+     * @param secondAction the second action shown in the notification
+     */
+    private void createNotification(@NonNull PrintJobInfo printJob, @Nullable Action firstAction,
+            @Nullable Action secondAction) {
         Notification.Builder builder = new Notification.Builder(mContext)
                 .setContentIntent(createContentIntent(printJob.getId()))
                 .setSmallIcon(computeNotificationIcon(printJob))
                 .setContentTitle(computeNotificationTitle(printJob))
-                .addAction(createCancelAction(printJob))
-                .setContentText(printJob.getPrinterName())
                 .setWhen(System.currentTimeMillis())
                 .setOngoing(true)
                 .setShowWhen(true)
                 .setColor(mContext.getColor(
                         com.android.internal.R.color.system_notification_accent_color));
+
+        if (firstAction != null) {
+            builder.addAction(firstAction);
+        }
+
+        if (secondAction != null) {
+            builder.addAction(secondAction);
+        }
+
+        if (printJob.getState() == PrintJobInfo.STATE_STARTED) {
+            float progress = printJob.getProgress();
+            if (progress >= 0) {
+                builder.setProgress(Integer.MAX_VALUE, (int)(Integer.MAX_VALUE * progress),
+                        false);
+            }
+        }
+
+        CharSequence status = printJob.getStatus();
+        if (status != null) {
+            builder.setContentText(status);
+        } else {
+            builder.setContentText(printJob.getPrinterName());
+        }
+
         mNotificationManager.notify(0, builder.build());
     }
 
+    private void createPrintingNotification(PrintJobInfo printJob) {
+        createNotification(printJob, createCancelAction(printJob), null);
+    }
+
     private void createFailedNotification(PrintJobInfo printJob) {
         Action.Builder restartActionBuilder = new Action.Builder(
                 Icon.createWithResource(mContext, R.drawable.ic_restart),
                 mContext.getString(R.string.restart), createRestartIntent(printJob.getId()));
 
-        Notification.Builder builder = new Notification.Builder(mContext)
-                .setContentIntent(createContentIntent(printJob.getId()))
-                .setSmallIcon(computeNotificationIcon(printJob))
-                .setContentTitle(computeNotificationTitle(printJob))
-                .addAction(createCancelAction(printJob))
-                .addAction(restartActionBuilder.build())
-                .setContentText(printJob.getPrinterName())
-                .setWhen(System.currentTimeMillis())
-                .setOngoing(true)
-                .setShowWhen(true)
-                .setColor(mContext.getColor(
-                        com.android.internal.R.color.system_notification_accent_color));
-        mNotificationManager.notify(0, builder.build());
+        createNotification(printJob, createCancelAction(printJob), restartActionBuilder.build());
     }
 
     private void createBlockedNotification(PrintJobInfo printJob) {
-        Notification.Builder builder = new Notification.Builder(mContext)
-                .setContentIntent(createContentIntent(printJob.getId()))
-                .setSmallIcon(computeNotificationIcon(printJob))
-                .setContentTitle(computeNotificationTitle(printJob))
-                .addAction(createCancelAction(printJob))
-                .setContentText(printJob.getPrinterName())
-                .setWhen(System.currentTimeMillis())
-                .setOngoing(true)
-                .setShowWhen(true)
-                .setColor(mContext.getColor(
-                        com.android.internal.R.color.system_notification_accent_color));
-           mNotificationManager.notify(0, builder.build());
+        createNotification(printJob, createCancelAction(printJob), null);
     }
 
     private void createCancellingNotification(PrintJobInfo printJob) {
-        Notification.Builder builder = new Notification.Builder(mContext)
-                .setContentIntent(createContentIntent(printJob.getId()))
-                .setSmallIcon(computeNotificationIcon(printJob))
-                .setContentTitle(computeNotificationTitle(printJob))
-                .setContentText(printJob.getPrinterName())
-                .setWhen(System.currentTimeMillis())
-                .setOngoing(true)
-                .setShowWhen(true)
-                .setColor(mContext.getColor(
-                        com.android.internal.R.color.system_notification_accent_color));
-        mNotificationManager.notify(0, builder.build());
+        createNotification(printJob, null, null);
     }
 
     private void createStackedNotification(List<PrintJobInfo> printJobs) {
diff --git a/packages/PrintSpooler/src/com/android/printspooler/model/PrintSpoolerService.java b/packages/PrintSpooler/src/com/android/printspooler/model/PrintSpoolerService.java
index 7adcfec..90eef83 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/model/PrintSpoolerService.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/model/PrintSpoolerService.java
@@ -16,6 +16,9 @@
 
 package com.android.printspooler.model;
 
+import android.annotation.FloatRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.Service;
 import android.content.ComponentName;
 import android.content.Context;
@@ -497,7 +500,7 @@
                 success = true;
 
                 printJob.setState(state);
-                printJob.setStateReason(error);
+                printJob.setStatus(error);
                 printJob.setCancelling(false);
 
                 if (DEBUG_PRINT_JOB_LIFECYCLE) {
@@ -547,6 +550,35 @@
         return success;
     }
 
+    /**
+     * Set the progress for a print job.
+     *
+     * @param printJobId ID of the print job to update
+     * @param progress the new progress
+     */
+    public void setProgress(@NonNull PrintJobId printJobId,
+            @FloatRange(from=0.0, to=1.0) float progress) {
+        synchronized (mLock) {
+            getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY).setProgress(progress);
+
+            mNotificationController.onUpdateNotifications(mPrintJobs);
+        }
+    }
+
+   /**
+    * Set the status for a print job.
+    *
+    * @param printJobId ID of the print job to update
+    * @param status the new status
+    */
+   public void setStatus(@NonNull PrintJobId printJobId, @Nullable CharSequence status) {
+       synchronized (mLock) {
+           getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY).setStatus(status);
+
+           mNotificationController.onUpdateNotifications(mPrintJobs);
+       }
+   }
+
     public boolean hasActivePrintJobsLocked() {
         final int printJobCount = mPrintJobs.size();
         for (int i = 0; i < printJobCount; i++) {
@@ -693,6 +725,8 @@
         private static final String ATTR_COPIES = "copies";
         private static final String ATTR_PRINTER_NAME = "printerName";
         private static final String ATTR_STATE_REASON = "stateReason";
+        private static final String ATTR_STATUS = "status";
+        private static final String ATTR_PROGRESS = "progress";
         private static final String ATTR_CANCELLING = "cancelling";
 
         private static final String TAG_ADVANCED_OPTIONS = "advancedOptions";
@@ -801,13 +835,19 @@
                     if (!TextUtils.isEmpty(printerName)) {
                         serializer.attribute(null, ATTR_PRINTER_NAME, printerName);
                     }
-                    String stateReason = printJob.getStateReason();
-                    if (!TextUtils.isEmpty(stateReason)) {
-                        serializer.attribute(null, ATTR_STATE_REASON, stateReason);
-                    }
                     serializer.attribute(null, ATTR_CANCELLING, String.valueOf(
                             printJob.isCancelling()));
 
+                    float progress = printJob.getProgress();
+                    if (progress != Float.NaN) {
+                        serializer.attribute(null, ATTR_PROGRESS, String.valueOf(progress));
+                    }
+
+                    CharSequence status = printJob.getStatus();
+                    if (!TextUtils.isEmpty(status)) {
+                        serializer.attribute(null, ATTR_STATUS, status.toString());
+                    }
+
                     PrinterId printerId = printJob.getPrinterId();
                     if (printerId != null) {
                         serializer.startTag(null, TAG_PRINTER_ID);
@@ -1025,8 +1065,25 @@
             printJob.setCopies(Integer.parseInt(copies));
             String printerName = parser.getAttributeValue(null, ATTR_PRINTER_NAME);
             printJob.setPrinterName(printerName);
+
+            String progressString = parser.getAttributeValue(null, ATTR_PROGRESS);
+            if (progressString != null) {
+                float progress = Float.parseFloat(progressString);
+
+                if (progress != -1) {
+                    printJob.setProgress(progress);
+                }
+            }
+
+            CharSequence status = parser.getAttributeValue(null, ATTR_STATUS);
+            printJob.setStatus(status);
+
+            // stateReason is deprecated, but might be used by old print jobs
             String stateReason = parser.getAttributeValue(null, ATTR_STATE_REASON);
-            printJob.setStateReason(stateReason);
+            if (stateReason != null) {
+                printJob.setStatus(stateReason);
+            }
+
             String cancelling = parser.getAttributeValue(null, ATTR_CANCELLING);
             printJob.setCancelling(!TextUtils.isEmpty(cancelling)
                     ? Boolean.parseBoolean(cancelling) : false);
@@ -1323,6 +1380,18 @@
                     .removeApprovedService(serviceToRemove);
         }
 
+        @Override
+        public void setProgress(@NonNull PrintJobId printJobId,
+                @FloatRange(from=0.0, to=1.0) float progress) throws RemoteException {
+            PrintSpoolerService.this.setProgress(printJobId, progress);
+        }
+
+        @Override
+        public void setStatus(@NonNull PrintJobId printJobId,
+                @Nullable CharSequence status) throws RemoteException {
+            PrintSpoolerService.this.setStatus(printJobId, status);
+        }
+
         public PrintSpoolerService getService() {
             return PrintSpoolerService.this;
         }
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/DashboardTile.java b/packages/SettingsLib/src/com/android/settingslib/drawer/DashboardTile.java
index 6fef134..347e9f7 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/DashboardTile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/DashboardTile.java
@@ -74,6 +74,11 @@
      */
     public int priority;
 
+    /**
+     * The metaData from the activity that defines this tile.
+     */
+    public Bundle metaData;
+
     public DashboardTile() {
         // Empty
     }
@@ -107,6 +112,7 @@
         dest.writeBundle(extras);
         dest.writeString(category);
         dest.writeInt(priority);
+        dest.writeBundle(metaData);
     }
 
     public void readFromParcel(Parcel in) {
@@ -125,6 +131,7 @@
         extras = in.readBundle();
         category = in.readString();
         priority = in.readInt();
+        metaData = in.readBundle();
     }
 
     DashboardTile(Parcel in) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
index 00dadb8..581c810 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
@@ -18,9 +18,15 @@
 import android.annotation.LayoutRes;
 import android.annotation.Nullable;
 import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.res.TypedArray;
+import android.os.AsyncTask;
 import android.os.Bundle;
 import android.support.v4.widget.DrawerLayout;
+import android.util.Log;
 import android.util.Pair;
 import android.view.Gravity;
 import android.view.LayoutInflater;
@@ -33,21 +39,30 @@
 import android.widget.Toolbar;
 import com.android.settingslib.R;
 
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 
 public class SettingsDrawerActivity extends Activity {
 
+    protected static final boolean DEBUG_TIMING = false;
+    private static final String TAG = "SettingsDrawerActivity";
+
+    private static List<DashboardCategory> sDashboardCategories;
+    private static HashMap<Pair<String, String>, DashboardTile> sTileCache;
+
+    private final PackageReceiver mPackageReceiver = new PackageReceiver();
+    private final List<CategoryListener> mCategoryListeners = new ArrayList<>();
+
     private SettingsDrawerAdapter mDrawerAdapter;
-    // Hold on to a cache of tiles to avoid loading the info multiple times.
-    private final HashMap<Pair<String, String>, DashboardTile> mTileCache = new HashMap<>();
-    private List<DashboardCategory> mDashboardCategories;
     private DrawerLayout mDrawerLayout;
 
     @Override
     protected void onCreate(@Nullable Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
+        long startTime = System.currentTimeMillis();
+
         requestWindowFeature(Window.FEATURE_NO_TITLE);
         super.setContentView(R.layout.settings_with_drawer);
         mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
@@ -62,6 +77,10 @@
             mDrawerLayout = null;
             return;
         }
+        if (sDashboardCategories == null) {
+            sTileCache = new HashMap<>();
+            sDashboardCategories = TileUtils.getCategories(this, sTileCache);
+        }
         setActionBar(toolbar);
         mDrawerAdapter = new SettingsDrawerAdapter(this);
         ListView listView = (ListView) findViewById(R.id.left_drawer);
@@ -72,6 +91,8 @@
                 onTileClicked(mDrawerAdapter.getTile(position));
             };
         });
+        if (DEBUG_TIMING) Log.d(TAG, "onCreate took " + (System.currentTimeMillis() - startTime)
+                + " ms");
     }
 
     @Override
@@ -88,7 +109,29 @@
     protected void onResume() {
         super.onResume();
 
-        updateDrawer();
+        final IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
+        filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+        filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+        filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
+        filter.addDataScheme("package");
+        registerReceiver(mPackageReceiver, filter);
+
+        new CategoriesUpdater().execute();
+    }
+
+    @Override
+    protected void onPause() {
+        unregisterReceiver(mPackageReceiver);
+
+        super.onPause();
+    }
+
+    public void addCategoryListener(CategoryListener listener) {
+        mCategoryListeners.add(listener);
+    }
+
+    public void remCategoryListener(CategoryListener listener) {
+        mCategoryListeners.remove(listener);
     }
 
     public void openDrawer() {
@@ -134,11 +177,16 @@
         }
     }
 
-    public List<DashboardCategory> getDashboardCategories(boolean force) {
-        if (force) {
-            mDashboardCategories = TileUtils.getCategories(this, mTileCache);
+    public List<DashboardCategory> getDashboardCategories() {
+        return sDashboardCategories;
+    }
+
+    protected void onCategoriesChanged() {
+        updateDrawer();
+        final int N = mCategoryListeners.size();
+        for (int i = 0; i < N; i++) {
+            mCategoryListeners.get(i).onCategoriesChanged();
         }
-        return mDashboardCategories;
     }
 
     public boolean openTile(DashboardTile tile) {
@@ -167,4 +215,28 @@
     public void onProfileTileOpen() {
         finish();
     }
+
+    public interface CategoryListener {
+        void onCategoriesChanged();
+    }
+
+    private class CategoriesUpdater extends AsyncTask<Void, Void, List<DashboardCategory>> {
+        @Override
+        protected List<DashboardCategory> doInBackground(Void... params) {
+            return TileUtils.getCategories(SettingsDrawerActivity.this, sTileCache);
+        }
+
+        @Override
+        protected void onPostExecute(List<DashboardCategory> dashboardCategories) {
+            sDashboardCategories = dashboardCategories;
+            onCategoriesChanged();
+        }
+    }
+
+    private class PackageReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            new CategoriesUpdater().execute();
+        }
+    }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerAdapter.java b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerAdapter.java
index fef716c..24c6ae8 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerAdapter.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerAdapter.java
@@ -38,7 +38,7 @@
     }
 
     void updateCategories() {
-        List<DashboardCategory> categories = mActivity.getDashboardCategories(true);
+        List<DashboardCategory> categories = mActivity.getDashboardCategories();
         mItems.clear();
         for (int i = 0; i < categories.size(); i++) {
             Item category = new Item();
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java b/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
index 18e8d31..6b36680 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
@@ -14,6 +14,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.res.Resources;
@@ -35,6 +36,7 @@
 public class TileUtils {
 
     private static final boolean DEBUG = false;
+    private static final boolean DEBUG_TIMING = true;
 
     private static final String LOG_TAG = "TileUtils";
 
@@ -105,12 +107,9 @@
 
     private static final String SETTING_PKG = "com.android.settings";
 
-    public static List<DashboardCategory> getCategories(Context context) {
-        return getCategories(context, new HashMap<Pair<String, String>, DashboardTile>());
-    }
-
     public static List<DashboardCategory> getCategories(Context context,
             HashMap<Pair<String, String>, DashboardTile> cache) {
+        final long startTime = System.currentTimeMillis();
         ArrayList<DashboardTile> tiles = new ArrayList<>();
         UserManager userManager = UserManager.get(context);
         for (UserHandle user : userManager.getUserProfiles()) {
@@ -143,6 +142,8 @@
             Collections.sort(category.tiles, TILE_COMPARATOR);
         }
         Collections.sort(categories, CATEGORY_COMPARATOR);
+        if (DEBUG_TIMING) Log.d(LOG_TAG, "getCategories took "
+                + (System.currentTimeMillis() - startTime) + " ms");
         return categories;
     }
 
@@ -207,7 +208,9 @@
                         activityInfo.packageName, activityInfo.name);
                 tile.category = categoryKey;
                 tile.priority = requireSettings ? resolved.priority : 0;
-                updateTileData(context, tile);
+                tile.metaData = activityInfo.metaData;
+                updateTileData(context, tile, activityInfo, activityInfo.applicationInfo,
+                        pm);
                 if (DEBUG) Log.d(LOG_TAG, "Adding tile " + tile.title);
 
                 addedCache.put(key, tile);
@@ -231,64 +234,52 @@
         return null;
     }
 
-    private static boolean updateTileData(Context context, DashboardTile tile) {
-        Intent intent = tile.intent;
-        if (intent != null) {
-            // Find the activity that is in the system image
-            PackageManager pm = context.getPackageManager();
-            List<ResolveInfo> list = tile.userHandle.size() != 0
-                    ? pm.queryIntentActivitiesAsUser(intent, PackageManager.GET_META_DATA,
-                            tile.userHandle.get(0).getIdentifier())
-                    : pm.queryIntentActivities(intent, PackageManager.GET_META_DATA);
-            int listSize = list.size();
-            for (int i = 0; i < listSize; i++) {
-                ResolveInfo resolveInfo = list.get(i);
-                if (resolveInfo.activityInfo.applicationInfo.isSystemApp()) {
-                    int icon = 0;
-                    CharSequence title = null;
-                    String summary = null;
+    private static boolean updateTileData(Context context, DashboardTile tile,
+            ActivityInfo activityInfo, ApplicationInfo applicationInfo, PackageManager pm) {
+        if (applicationInfo.isSystemApp()) {
+            int icon = 0;
+            CharSequence title = null;
+            String summary = null;
 
-                    // Get the activity's meta-data
-                    try {
-                        Resources res = pm.getResourcesForApplication(
-                                resolveInfo.activityInfo.packageName);
-                        Bundle metaData = resolveInfo.activityInfo.metaData;
+            // Get the activity's meta-data
+            try {
+                Resources res = pm.getResourcesForApplication(
+                        applicationInfo.packageName);
+                Bundle metaData = activityInfo.metaData;
 
-                        if (res != null && metaData != null) {
-                            if (metaData.containsKey(META_DATA_PREFERENCE_ICON)) {
-                                icon = metaData.getInt(META_DATA_PREFERENCE_ICON);
-                            }
-                            if (metaData.containsKey(META_DATA_PREFERENCE_TITLE)) {
-                                title = metaData.getString(META_DATA_PREFERENCE_TITLE);
-                            }
-                            if (metaData.containsKey(META_DATA_PREFERENCE_SUMMARY)) {
-                                summary = metaData.getString(META_DATA_PREFERENCE_SUMMARY);
-                            }
-                        }
-                    } catch (PackageManager.NameNotFoundException | Resources.NotFoundException e) {
-                        if (DEBUG) Log.d(LOG_TAG, "Couldn't find info", e);
+                if (res != null && metaData != null) {
+                    if (metaData.containsKey(META_DATA_PREFERENCE_ICON)) {
+                        icon = metaData.getInt(META_DATA_PREFERENCE_ICON);
                     }
-
-                    // Set the preference title to the activity's label if no
-                    // meta-data is found
-                    if (TextUtils.isEmpty(title)) {
-                        title = resolveInfo.loadLabel(pm).toString();
+                    if (metaData.containsKey(META_DATA_PREFERENCE_TITLE)) {
+                        title = metaData.getString(META_DATA_PREFERENCE_TITLE);
                     }
-                    if (icon == 0) {
-                        icon = resolveInfo.activityInfo.icon;
+                    if (metaData.containsKey(META_DATA_PREFERENCE_SUMMARY)) {
+                        summary = metaData.getString(META_DATA_PREFERENCE_SUMMARY);
                     }
-
-                    // Set icon, title and summary for the preference
-                    tile.icon = Icon.createWithResource(resolveInfo.activityInfo.packageName, icon);
-                    tile.title = title;
-                    tile.summary = summary;
-                    // Replace the intent with this specific activity
-                    tile.intent = new Intent().setClassName(resolveInfo.activityInfo.packageName,
-                            resolveInfo.activityInfo.name);
-
-                    return true;
                 }
+            } catch (PackageManager.NameNotFoundException | Resources.NotFoundException e) {
+                if (DEBUG) Log.d(LOG_TAG, "Couldn't find info", e);
             }
+
+            // Set the preference title to the activity's label if no
+            // meta-data is found
+            if (TextUtils.isEmpty(title)) {
+                title = activityInfo.loadLabel(pm).toString();
+            }
+            if (icon == 0) {
+                icon = activityInfo.icon;
+            }
+
+            // Set icon, title and summary for the preference
+            tile.icon = Icon.createWithResource(activityInfo.packageName, icon);
+            tile.title = title;
+            tile.summary = summary;
+            // Replace the intent with this specific activity
+            tile.intent = new Intent().setClassName(activityInfo.packageName,
+                    activityInfo.name);
+
+            return true;
         }
 
         return false;
diff --git a/packages/Shell/res/values-af/strings.xml b/packages/Shell/res/values-af/strings.xml
index d1998bd..8430bf0 100644
--- a/packages/Shell/res/values-af/strings.xml
+++ b/packages/Shell/res/values-af/strings.xml
@@ -17,6 +17,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="3701846017049540910">"Tuisskerm"</string>
+    <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Besig met foutverslag"</string>
     <string name="bugreport_finished_title" msgid="2293711546892863898">"Foutverslag vasgevang"</string>
     <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Swiep na links om jou foutverslag te deel"</string>
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Raak om jou foutverslag te deel"</string>
diff --git a/packages/Shell/res/values-am/strings.xml b/packages/Shell/res/values-am/strings.xml
index 6f905a9..9400f37 100644
--- a/packages/Shell/res/values-am/strings.xml
+++ b/packages/Shell/res/values-am/strings.xml
@@ -17,6 +17,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="3701846017049540910">"ቀፎ"</string>
+    <string name="bugreport_in_progress_title" msgid="6125357428413919520">"የሳንካ ሪፓርት በሂደት ላይ"</string>
     <string name="bugreport_finished_title" msgid="2293711546892863898">"የሳንካ ሪፖርት ተይዟል"</string>
     <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"የሳንካ ሪፖርትዎን ለማጋራት ወደ ግራ ያንሸራትቱ"</string>
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"የሳንካ ሪፖርትዎን ለማጋራት ይንክኩ"</string>
diff --git a/packages/Shell/res/values-ar/strings.xml b/packages/Shell/res/values-ar/strings.xml
index 76100b5..2161a76 100644
--- a/packages/Shell/res/values-ar/strings.xml
+++ b/packages/Shell/res/values-ar/strings.xml
@@ -17,6 +17,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="3701846017049540910">"Shell"</string>
+    <string name="bugreport_in_progress_title" msgid="6125357428413919520">"تقرير الخطأ قيد التقدم"</string>
     <string name="bugreport_finished_title" msgid="2293711546892863898">"تم الحصول على تقرير الأخطاء"</string>
     <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"مرر بسرعة لليمين لمشاركة تقرير الخطأ"</string>
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"المس لمشاركة تقرير الأخطاء"</string>
diff --git a/packages/Shell/res/values-az-rAZ/strings.xml b/packages/Shell/res/values-az-rAZ/strings.xml
index d8176f5..76ee4bf 100644
--- a/packages/Shell/res/values-az-rAZ/strings.xml
+++ b/packages/Shell/res/values-az-rAZ/strings.xml
@@ -17,6 +17,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="3701846017049540910">"Shell"</string>
+    <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Baq hesabatı davam edir"</string>
     <string name="bugreport_finished_title" msgid="2293711546892863898">"Baq raport alındı"</string>
     <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Baq raportunu paylaşmaq üçün sola sürüşdürün"</string>
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Xətanı şikayətini paylaşmaq üçün toxunun"</string>
diff --git a/packages/Shell/res/values-bg/strings.xml b/packages/Shell/res/values-bg/strings.xml
index fc2dad0..bbfae69 100644
--- a/packages/Shell/res/values-bg/strings.xml
+++ b/packages/Shell/res/values-bg/strings.xml
@@ -17,6 +17,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="3701846017049540910">"Команден ред"</string>
+    <!-- no translation found for bugreport_in_progress_title (6125357428413919520) -->
+    <skip />
     <string name="bugreport_finished_title" msgid="2293711546892863898">"Отчетът за програмни грешки е записан"</string>
     <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Прекарайте пръст наляво, за да споделите сигнала си за програмна грешка"</string>
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Докоснете, за да споделите отчета си за програмни грешки"</string>
diff --git a/packages/Shell/res/values-bn-rBD/strings.xml b/packages/Shell/res/values-bn-rBD/strings.xml
index 5aa3e9d..5ca5835 100644
--- a/packages/Shell/res/values-bn-rBD/strings.xml
+++ b/packages/Shell/res/values-bn-rBD/strings.xml
@@ -17,6 +17,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="3701846017049540910">"শেল"</string>
+    <string name="bugreport_in_progress_title" msgid="6125357428413919520">"ত্রুটির প্রতিবেদন করা হচ্ছে"</string>
     <string name="bugreport_finished_title" msgid="2293711546892863898">"ত্রুটির প্রতিবেদন নেওয়া হয়েছে"</string>
     <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"আপনার বাগ রিপোর্ট শেয়ার করতে বামে সোয়াইপ করুন"</string>
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"আপনার ত্রুটির প্রতিবেদন ভাগ করতে স্পর্শ করুন"</string>
diff --git a/packages/Shell/res/values-ca/strings.xml b/packages/Shell/res/values-ca/strings.xml
index bef1a67..1e6ec53 100644
--- a/packages/Shell/res/values-ca/strings.xml
+++ b/packages/Shell/res/values-ca/strings.xml
@@ -17,6 +17,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="3701846017049540910">"Protecció"</string>
+    <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Informe d\'errors en curs"</string>
     <string name="bugreport_finished_title" msgid="2293711546892863898">"S\'ha registrat l\'informe d\'error"</string>
     <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Llisca cap a l\'esquerra per compartir l\'informe d\'errors."</string>
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Toca aquí per compartir el teu informe d\'error."</string>
diff --git a/packages/Shell/res/values-cs/strings.xml b/packages/Shell/res/values-cs/strings.xml
index 46c8f4e..336c21f 100644
--- a/packages/Shell/res/values-cs/strings.xml
+++ b/packages/Shell/res/values-cs/strings.xml
@@ -17,6 +17,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="3701846017049540910">"Shell"</string>
+    <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Probíhá zpracování zprávy o chybě"</string>
     <string name="bugreport_finished_title" msgid="2293711546892863898">"Bylo vytvořeno chybové hlášení"</string>
     <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Chcete-li hlášení chyby sdílet, přejeďte doleva."</string>
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Chybové hlášení můžete sdílet klepnutím."</string>
diff --git a/packages/Shell/res/values-da/strings.xml b/packages/Shell/res/values-da/strings.xml
index 7a87b9b..6f65894 100644
--- a/packages/Shell/res/values-da/strings.xml
+++ b/packages/Shell/res/values-da/strings.xml
@@ -17,6 +17,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="3701846017049540910">"Shell"</string>
+    <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Fejlrapporten er under udførelse"</string>
     <string name="bugreport_finished_title" msgid="2293711546892863898">"Fejlrapporten er registreret"</string>
     <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Stryg til venstre for at dele din fejlrapport"</string>
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Tryk for at dele din fejlrapport"</string>
diff --git a/packages/Shell/res/values-de/strings.xml b/packages/Shell/res/values-de/strings.xml
index 7a25c4d..03c6166 100644
--- a/packages/Shell/res/values-de/strings.xml
+++ b/packages/Shell/res/values-de/strings.xml
@@ -17,6 +17,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="3701846017049540910">"Shell"</string>
+    <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Fehlerbericht in Bearbeitung"</string>
     <string name="bugreport_finished_title" msgid="2293711546892863898">"Fehlerbericht erfasst"</string>
     <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Wischen Sie nach links, um Ihren Fehlerbericht zu teilen."</string>
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Tippen, um Fehlerbericht zu teilen"</string>
diff --git a/packages/Shell/res/values-el/strings.xml b/packages/Shell/res/values-el/strings.xml
index 02e37f7..ceec189 100644
--- a/packages/Shell/res/values-el/strings.xml
+++ b/packages/Shell/res/values-el/strings.xml
@@ -17,6 +17,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="3701846017049540910">"Κέλυφος"</string>
+    <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Αναφορά σφάλματος σε εξέλιξη"</string>
     <string name="bugreport_finished_title" msgid="2293711546892863898">"Η λήψη της αναφοράς ήταν επιτυχής"</string>
     <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Σύρετε προς τα αριστερά για κοινή χρήση της αναφοράς σφαλμάτων"</string>
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Αγγίξτε για να μοιραστείτε τη αναφορά σφαλμάτων"</string>
diff --git a/packages/Shell/res/values-en-rAU/strings.xml b/packages/Shell/res/values-en-rAU/strings.xml
index 1b55115..99c9f4f 100644
--- a/packages/Shell/res/values-en-rAU/strings.xml
+++ b/packages/Shell/res/values-en-rAU/strings.xml
@@ -17,6 +17,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="3701846017049540910">"Shell"</string>
+    <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Bug report in progress"</string>
     <string name="bugreport_finished_title" msgid="2293711546892863898">"Bug report captured"</string>
     <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Swipe left to share your bug report"</string>
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Touch to share your bug report"</string>
diff --git a/packages/Shell/res/values-en-rGB/strings.xml b/packages/Shell/res/values-en-rGB/strings.xml
index 1b55115..99c9f4f 100644
--- a/packages/Shell/res/values-en-rGB/strings.xml
+++ b/packages/Shell/res/values-en-rGB/strings.xml
@@ -17,6 +17,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="3701846017049540910">"Shell"</string>
+    <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Bug report in progress"</string>
     <string name="bugreport_finished_title" msgid="2293711546892863898">"Bug report captured"</string>
     <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Swipe left to share your bug report"</string>
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Touch to share your bug report"</string>
diff --git a/packages/Shell/res/values-en-rIN/strings.xml b/packages/Shell/res/values-en-rIN/strings.xml
index 1b55115..99c9f4f 100644
--- a/packages/Shell/res/values-en-rIN/strings.xml
+++ b/packages/Shell/res/values-en-rIN/strings.xml
@@ -17,6 +17,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="3701846017049540910">"Shell"</string>
+    <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Bug report in progress"</string>
     <string name="bugreport_finished_title" msgid="2293711546892863898">"Bug report captured"</string>
     <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Swipe left to share your bug report"</string>
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Touch to share your bug report"</string>
diff --git a/packages/Shell/res/values-es-rUS/strings.xml b/packages/Shell/res/values-es-rUS/strings.xml
index 1937349..263459e 100644
--- a/packages/Shell/res/values-es-rUS/strings.xml
+++ b/packages/Shell/res/values-es-rUS/strings.xml
@@ -17,6 +17,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="3701846017049540910">"Shell"</string>
+    <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Informe de errores en progreso"</string>
     <string name="bugreport_finished_title" msgid="2293711546892863898">"Informe de errores capturado"</string>
     <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Desliza el dedo hacia la izquierda para compartir el informe de errores."</string>
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Toca para compartir tu informe de errores."</string>
diff --git a/packages/Shell/res/values-es/strings.xml b/packages/Shell/res/values-es/strings.xml
index 002bea9..3b37d40 100644
--- a/packages/Shell/res/values-es/strings.xml
+++ b/packages/Shell/res/values-es/strings.xml
@@ -17,6 +17,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="3701846017049540910">"Shell"</string>
+    <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Informe de errores en curso"</string>
     <string name="bugreport_finished_title" msgid="2293711546892863898">"Informe de error registrado"</string>
     <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Desliza el dedo hacia la izquierda para compartir el informe de error"</string>
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Toca para compartir tu informe de error"</string>
diff --git a/packages/Shell/res/values-et-rEE/strings.xml b/packages/Shell/res/values-et-rEE/strings.xml
index a16875c..652de64 100644
--- a/packages/Shell/res/values-et-rEE/strings.xml
+++ b/packages/Shell/res/values-et-rEE/strings.xml
@@ -17,6 +17,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="3701846017049540910">"Kest"</string>
+    <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Veaaruande töötlemine on pooleli"</string>
     <string name="bugreport_finished_title" msgid="2293711546892863898">"Veaaruanne jäädvustati"</string>
     <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Veaaruande jagamiseks pühkige vasakule"</string>
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Veaaruande jagamiseks puudutage"</string>
diff --git a/packages/Shell/res/values-eu-rES/strings.xml b/packages/Shell/res/values-eu-rES/strings.xml
index e7f1766..3234786 100644
--- a/packages/Shell/res/values-eu-rES/strings.xml
+++ b/packages/Shell/res/values-eu-rES/strings.xml
@@ -17,6 +17,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="3701846017049540910">"Shell-interfazea"</string>
+    <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Abian da akatsen txostena egiteko prozesua"</string>
     <string name="bugreport_finished_title" msgid="2293711546892863898">"Akatsen txostena jaso da"</string>
     <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Programa-akatsen txostena partekatzeko, pasatu hatza ezkerrera"</string>
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Akatsen txostena partekatzeko, ukitu"</string>
diff --git a/packages/Shell/res/values-fa/strings.xml b/packages/Shell/res/values-fa/strings.xml
index 9138b28..ff58c25 100644
--- a/packages/Shell/res/values-fa/strings.xml
+++ b/packages/Shell/res/values-fa/strings.xml
@@ -17,6 +17,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="3701846017049540910">"Shell"</string>
+    <string name="bugreport_in_progress_title" msgid="6125357428413919520">"گزارش اشکال در حال انجام است"</string>
     <string name="bugreport_finished_title" msgid="2293711546892863898">"گزارش اشکال دریافت شد"</string>
     <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"برای اشتراک‌گذاری گزارش اشکال، به تندی آن را به چپ بکشید"</string>
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"جهت اشتراک‌گذاری گزارش اشکال خود لمس کنید"</string>
diff --git a/packages/Shell/res/values-fi/strings.xml b/packages/Shell/res/values-fi/strings.xml
index 079433d..df6851c 100644
--- a/packages/Shell/res/values-fi/strings.xml
+++ b/packages/Shell/res/values-fi/strings.xml
@@ -17,6 +17,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="3701846017049540910">"Komentotulkki"</string>
+    <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Virheraportti käynnissä"</string>
     <string name="bugreport_finished_title" msgid="2293711546892863898">"Virheraportti tallennettu"</string>
     <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Jaa virheraportti pyyhkäisemällä vasemmalle"</string>
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Jaa virheraportti koskettamalla tätä"</string>
diff --git a/packages/Shell/res/values-fr-rCA/strings.xml b/packages/Shell/res/values-fr-rCA/strings.xml
index 4ead085..5f58164 100644
--- a/packages/Shell/res/values-fr-rCA/strings.xml
+++ b/packages/Shell/res/values-fr-rCA/strings.xml
@@ -17,6 +17,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="3701846017049540910">"Shell"</string>
+    <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Création du rapport de bogue en cours..."</string>
     <string name="bugreport_finished_title" msgid="2293711546892863898">"Rapport de bogue enregistré"</string>
     <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Faites glisser le doigt vers la gauche pour partager votre rapport de bogue."</string>
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Appuyer ici pour partager votre rapport de bogue"</string>
diff --git a/packages/Shell/res/values-fr/strings.xml b/packages/Shell/res/values-fr/strings.xml
index 14c1d3b..9562c6c 100644
--- a/packages/Shell/res/values-fr/strings.xml
+++ b/packages/Shell/res/values-fr/strings.xml
@@ -17,6 +17,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="3701846017049540910">"Shell"</string>
+    <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Rapport de bug en cours"</string>
     <string name="bugreport_finished_title" msgid="2293711546892863898">"Rapport de bug enregistré"</string>
     <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Faites glisser le doigt vers la gauche pour partager votre rapport d\'erreur."</string>
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Appuyez ici pour partager le rapport de bug"</string>
diff --git a/packages/Shell/res/values-gl-rES/strings.xml b/packages/Shell/res/values-gl-rES/strings.xml
index 61b0335..1f8da5d 100644
--- a/packages/Shell/res/values-gl-rES/strings.xml
+++ b/packages/Shell/res/values-gl-rES/strings.xml
@@ -17,6 +17,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="3701846017049540910">"Shell"</string>
+    <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Informe de erro en curso"</string>
     <string name="bugreport_finished_title" msgid="2293711546892863898">"Informe de erros rexistrado"</string>
     <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Pasa o dedo á esquerda para compartir o teu informe de erros"</string>
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Toca aquí para compartir o teu informe de erros"</string>
diff --git a/packages/Shell/res/values-gu-rIN/strings.xml b/packages/Shell/res/values-gu-rIN/strings.xml
index 746ac45..7a94dd7 100644
--- a/packages/Shell/res/values-gu-rIN/strings.xml
+++ b/packages/Shell/res/values-gu-rIN/strings.xml
@@ -17,6 +17,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="3701846017049540910">"શેલ"</string>
+    <string name="bugreport_in_progress_title" msgid="6125357428413919520">"બગ રિપોર્ટ ચાલુ છે"</string>
     <string name="bugreport_finished_title" msgid="2293711546892863898">"બગ રિપોર્ટ કેપ્ચર કરી"</string>
     <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"તમારી બગ રિપોર્ટ શેર કરવા માટે ડાબે સ્વાઇપ કરો"</string>
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"તમારી બગ રિપોર્ટ શેર કરવા માટે ટચ કરો"</string>
diff --git a/packages/Shell/res/values-hi/strings.xml b/packages/Shell/res/values-hi/strings.xml
index 97db607..273b484 100644
--- a/packages/Shell/res/values-hi/strings.xml
+++ b/packages/Shell/res/values-hi/strings.xml
@@ -17,6 +17,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="3701846017049540910">"शेल"</string>
+    <string name="bugreport_in_progress_title" msgid="6125357428413919520">"बग रिपोर्ट प्रगति में है"</string>
     <string name="bugreport_finished_title" msgid="2293711546892863898">"बग रिपोर्ट कैप्चर कर ली गई"</string>
     <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"अपनी बग रिपोर्ट साझा करने के लिए बाएं स्वाइप करें"</string>
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"अपनी बग रिपोर्ट साझा करने के लिए स्पर्श करें"</string>
diff --git a/packages/Shell/res/values-hr/strings.xml b/packages/Shell/res/values-hr/strings.xml
index b1fad84..3836edb 100644
--- a/packages/Shell/res/values-hr/strings.xml
+++ b/packages/Shell/res/values-hr/strings.xml
@@ -17,6 +17,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="3701846017049540910">"Ljuska"</string>
+    <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Izvješće o programskoj pogrešci u tijeku"</string>
     <string name="bugreport_finished_title" msgid="2293711546892863898">"Prijava programske pogreške snimljena je"</string>
     <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Prijeđite prstom ulijevo da biste poslali izvješće o programskim pogreškama"</string>
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Dodirnite za dijeljenje prijave programske pogreške"</string>
diff --git a/packages/Shell/res/values-hu/strings.xml b/packages/Shell/res/values-hu/strings.xml
index 7dae6c1..9191e02 100644
--- a/packages/Shell/res/values-hu/strings.xml
+++ b/packages/Shell/res/values-hu/strings.xml
@@ -17,6 +17,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="3701846017049540910">"Héj"</string>
+    <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Hibajelentés folyamatban"</string>
     <string name="bugreport_finished_title" msgid="2293711546892863898">"Programhiba-jelentés rögzítve"</string>
     <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Húzza ujját balra a hibajelentés megosztásához"</string>
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Érintse meg a programhiba-jelentés megosztásához"</string>
diff --git a/packages/Shell/res/values-hy-rAM/strings.xml b/packages/Shell/res/values-hy-rAM/strings.xml
index 149b677..bfa9b04 100644
--- a/packages/Shell/res/values-hy-rAM/strings.xml
+++ b/packages/Shell/res/values-hy-rAM/strings.xml
@@ -17,6 +17,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="3701846017049540910">"Խեցի"</string>
+    <!-- no translation found for bugreport_in_progress_title (6125357428413919520) -->
+    <skip />
     <string name="bugreport_finished_title" msgid="2293711546892863898">"Վրիպակի զեկույց է ստացվել"</string>
     <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Սահեցրեք ձախ՝ սխալի հաշվետվությունը համօգտագործելու համար"</string>
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Հպեք` ձեր վրիպակի մասին զեկույցը տարածելու համար"</string>
diff --git a/packages/Shell/res/values-in/strings.xml b/packages/Shell/res/values-in/strings.xml
index 534badc..9a13d8b 100644
--- a/packages/Shell/res/values-in/strings.xml
+++ b/packages/Shell/res/values-in/strings.xml
@@ -17,6 +17,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="3701846017049540910">"Kerangka"</string>
+    <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Laporan bug sedang berlangsung"</string>
     <string name="bugreport_finished_title" msgid="2293711546892863898">"Laporan bug tercatat"</string>
     <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Gesek ke kiri untuk membagikan laporan bug Anda"</string>
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Sentuh untuk membagikan laporan bug Anda"</string>
diff --git a/packages/Shell/res/values-is-rIS/strings.xml b/packages/Shell/res/values-is-rIS/strings.xml
index ce742f6..4304c8e 100644
--- a/packages/Shell/res/values-is-rIS/strings.xml
+++ b/packages/Shell/res/values-is-rIS/strings.xml
@@ -17,6 +17,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="3701846017049540910">"Skipanalína"</string>
+    <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Verið er að útbúa villutilkynningu"</string>
     <string name="bugreport_finished_title" msgid="2293711546892863898">"Villutilkynning útbúin"</string>
     <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Strjúktu til vinstri til að deila villuskýrslunni"</string>
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Snertu til að deila villutilkynningunni"</string>
diff --git a/packages/Shell/res/values-it/strings.xml b/packages/Shell/res/values-it/strings.xml
index 38e360f..63d39d0 100644
--- a/packages/Shell/res/values-it/strings.xml
+++ b/packages/Shell/res/values-it/strings.xml
@@ -17,6 +17,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="3701846017049540910">"Shell"</string>
+    <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Segnalazione di bug in corso"</string>
     <string name="bugreport_finished_title" msgid="2293711546892863898">"Segnalazione di bug acquisita"</string>
     <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Scorri verso sinistra per condividere il rapporto sui bug"</string>
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Tocca per condividere la segnalazione di bug"</string>
diff --git a/packages/Shell/res/values-iw/strings.xml b/packages/Shell/res/values-iw/strings.xml
index ab9aecf..94a167f 100644
--- a/packages/Shell/res/values-iw/strings.xml
+++ b/packages/Shell/res/values-iw/strings.xml
@@ -17,6 +17,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="3701846017049540910">"מעטפת"</string>
+    <string name="bugreport_in_progress_title" msgid="6125357428413919520">"הדוח על הבאג מתבצע כעת"</string>
     <string name="bugreport_finished_title" msgid="2293711546892863898">"דוח הבאגים צולם"</string>
     <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"החלק שמאלה כדי לשתף את דוח הבאגים"</string>
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"גע כדי לשתף את דוח הבאגים שלך"</string>
diff --git a/packages/Shell/res/values-ja/strings.xml b/packages/Shell/res/values-ja/strings.xml
index 619ee4f..77adb37 100644
--- a/packages/Shell/res/values-ja/strings.xml
+++ b/packages/Shell/res/values-ja/strings.xml
@@ -17,6 +17,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="3701846017049540910">"シェル"</string>
+    <string name="bugreport_in_progress_title" msgid="6125357428413919520">"バグレポートを処理しています"</string>
     <string name="bugreport_finished_title" msgid="2293711546892863898">"バグレポートが記録されました"</string>
     <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"バグレポートを共有するには左にスワイプ"</string>
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"バグレポートを共有するにはタップします"</string>
diff --git a/packages/Shell/res/values-ka-rGE/strings.xml b/packages/Shell/res/values-ka-rGE/strings.xml
index f98045f..2521d3e 100644
--- a/packages/Shell/res/values-ka-rGE/strings.xml
+++ b/packages/Shell/res/values-ka-rGE/strings.xml
@@ -17,6 +17,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="3701846017049540910">"გარეკანი"</string>
+    <!-- no translation found for bugreport_in_progress_title (6125357428413919520) -->
+    <skip />
     <string name="bugreport_finished_title" msgid="2293711546892863898">"ანგარიში ხარვეზების შესახებ შექმნილია"</string>
     <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"გაასრიალეთ მარცხნივ თქვენი ხარვეზის შეტყობინების გასაზიარებლად"</string>
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"შეეხეთ თქვენი ხარვეზების ანგარიშის გასაზიარებლად"</string>
diff --git a/packages/Shell/res/values-kk-rKZ/strings.xml b/packages/Shell/res/values-kk-rKZ/strings.xml
index a24f2cc..5ce7815a 100644
--- a/packages/Shell/res/values-kk-rKZ/strings.xml
+++ b/packages/Shell/res/values-kk-rKZ/strings.xml
@@ -17,6 +17,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="3701846017049540910">"Қабыршық"</string>
+    <!-- no translation found for bugreport_in_progress_title (6125357428413919520) -->
+    <skip />
     <string name="bugreport_finished_title" msgid="2293711546892863898">"Вирус туралы баянат қабылданды"</string>
     <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Қате туралы есепті бөлісу үшін солға жанаңыз"</string>
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Бөліс үшін, вирус туралы баянатты түртіңіз."</string>
diff --git a/packages/Shell/res/values-km-rKH/strings.xml b/packages/Shell/res/values-km-rKH/strings.xml
index 2439248..2814a73 100644
--- a/packages/Shell/res/values-km-rKH/strings.xml
+++ b/packages/Shell/res/values-km-rKH/strings.xml
@@ -17,6 +17,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="3701846017049540910">"សែល"</string>
+    <string name="bugreport_in_progress_title" msgid="6125357428413919520">"កំពុងដំណើរការរបាយការណ៍កំហុស"</string>
     <string name="bugreport_finished_title" msgid="2293711546892863898">"បាន​ចាប់​យក​របាយការណ៍​កំហុស"</string>
     <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"អូស​ទៅ​ឆ្វេង​​ ដើម្បី​ចែក​រំលែក​របាយការណ៍​កំហុស​របស់​អ្នក"</string>
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"ប៉ះ​ ដើម្បី​ចែក​រំលែក​របាយការណ៍​កំហុស​របស់​អ្នក"</string>
diff --git a/packages/Shell/res/values-kn-rIN/strings.xml b/packages/Shell/res/values-kn-rIN/strings.xml
index bca9c45..be34c85 100644
--- a/packages/Shell/res/values-kn-rIN/strings.xml
+++ b/packages/Shell/res/values-kn-rIN/strings.xml
@@ -17,6 +17,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="3701846017049540910">"ಶೆಲ್"</string>
+    <string name="bugreport_in_progress_title" msgid="6125357428413919520">"ದೋಷ ವರದಿ ಪ್ರಗತಿಯಲ್ಲಿದೆ"</string>
     <string name="bugreport_finished_title" msgid="2293711546892863898">"ದೋಷದ ವರದಿಯನ್ನು ಸೆರೆಹಿಡಿಯಲಾಗಿದೆ"</string>
     <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"ನಿಮ್ಮ ದೋಷ ವರದಿಯನ್ನು ಹಂಚಿಕೊಳ್ಳಲು ಎಡಕ್ಕೆ ಸ್ವೈಪ್‌ ಮಾಡಿ"</string>
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"ನಿಮ್ಮ ದೋಷದ ವರದಿಯನ್ನು ಹಂಚಿಕೊಳ್ಳಲು ಸ್ಪರ್ಶಿಸಿ"</string>
diff --git a/packages/Shell/res/values-ko/strings.xml b/packages/Shell/res/values-ko/strings.xml
index a3a0bf3..46c0daa 100644
--- a/packages/Shell/res/values-ko/strings.xml
+++ b/packages/Shell/res/values-ko/strings.xml
@@ -17,6 +17,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="3701846017049540910">"셸"</string>
+    <!-- no translation found for bugreport_in_progress_title (6125357428413919520) -->
+    <skip />
     <string name="bugreport_finished_title" msgid="2293711546892863898">"버그 신고서 캡처됨"</string>
     <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"왼쪽으로 스와이프하여 버그 신고서를 공유하세요."</string>
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"버그 신고서를 공유하려면 터치하세요."</string>
diff --git a/packages/Shell/res/values-ky-rKG/strings.xml b/packages/Shell/res/values-ky-rKG/strings.xml
index 622920a..aa8c538 100644
--- a/packages/Shell/res/values-ky-rKG/strings.xml
+++ b/packages/Shell/res/values-ky-rKG/strings.xml
@@ -17,6 +17,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="3701846017049540910">"Командалык кабык"</string>
+    <!-- no translation found for bugreport_in_progress_title (6125357428413919520) -->
+    <skip />
     <string name="bugreport_finished_title" msgid="2293711546892863898">"Ката тууралуу билдирүү түзүлдү"</string>
     <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Ката жөнүндө кабар менен бөлүшүү үчүн солго серпип коюңуз"</string>
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Ката тууралуу билдирүүңүздү жөнөтүш үчүн, тийиңиз"</string>
diff --git a/packages/Shell/res/values-lo-rLA/strings.xml b/packages/Shell/res/values-lo-rLA/strings.xml
index 537f197..caf11c4 100644
--- a/packages/Shell/res/values-lo-rLA/strings.xml
+++ b/packages/Shell/res/values-lo-rLA/strings.xml
@@ -17,6 +17,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="3701846017049540910">"Shell"</string>
+    <string name="bugreport_in_progress_title" msgid="6125357428413919520">"ລາຍງານບັນຫາພວມດຳເນີນຢູ່"</string>
     <string name="bugreport_finished_title" msgid="2293711546892863898">"ລາຍງານຈຸດບົກພ່ອງຖືກເກັບກຳແລ້ວ"</string>
     <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"​ປັດ​ໄປ​ຊ້າຍ​ເພື່ອ​ສົ່ງ​ລາຍ​ງານ​ຂໍ້​ຜິດ​ພາດ​ຂອງ​ທ່ານ"</string>
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"ແຕະເພື່ອສົ່ງການລາຍງານປັນຫາຂອງທ່ານ"</string>
diff --git a/packages/Shell/res/values-lt/strings.xml b/packages/Shell/res/values-lt/strings.xml
index 6965e4c..e70cf9f 100644
--- a/packages/Shell/res/values-lt/strings.xml
+++ b/packages/Shell/res/values-lt/strings.xml
@@ -17,6 +17,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="3701846017049540910">"Apvalkalas"</string>
+    <!-- no translation found for bugreport_in_progress_title (6125357428413919520) -->
+    <skip />
     <string name="bugreport_finished_title" msgid="2293711546892863898">"Riktų ataskaita užfiksuota"</string>
     <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Perbraukite kairėn, kad bendrintumėte rikto ataskaitą"</string>
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Palieskite, kad bendrintumėte riktų ataskaitą"</string>
diff --git a/packages/Shell/res/values-lv/strings.xml b/packages/Shell/res/values-lv/strings.xml
index efc40f3..c5d10bb 100644
--- a/packages/Shell/res/values-lv/strings.xml
+++ b/packages/Shell/res/values-lv/strings.xml
@@ -17,6 +17,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="3701846017049540910">"Aizsargs"</string>
+    <!-- no translation found for bugreport_in_progress_title (6125357428413919520) -->
+    <skip />
     <string name="bugreport_finished_title" msgid="2293711546892863898">"Izveidots kļūdu pārskats"</string>
     <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Velciet pa kreisi, lai kopīgotu savu kļūdu ziņojumu."</string>
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Pieskarieties, lai kopīgotu kļūdu pārskatu."</string>
diff --git a/packages/Shell/res/values-mk-rMK/strings.xml b/packages/Shell/res/values-mk-rMK/strings.xml
index 7571c2c..4baa86a 100644
--- a/packages/Shell/res/values-mk-rMK/strings.xml
+++ b/packages/Shell/res/values-mk-rMK/strings.xml
@@ -17,6 +17,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="3701846017049540910">"Обвивка"</string>
+    <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Извештајот за грешка е во тек"</string>
     <string name="bugreport_finished_title" msgid="2293711546892863898">"Извештајот за грешка е снимен"</string>
     <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Повлечете налево за да споделите пријава за грешка"</string>
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Допри да се сподели твојот извештај за грешка"</string>
diff --git a/packages/Shell/res/values-ml-rIN/strings.xml b/packages/Shell/res/values-ml-rIN/strings.xml
index 4779e4d..5dd7763 100644
--- a/packages/Shell/res/values-ml-rIN/strings.xml
+++ b/packages/Shell/res/values-ml-rIN/strings.xml
@@ -17,6 +17,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="3701846017049540910">"ഷെൽ"</string>
+    <string name="bugreport_in_progress_title" msgid="6125357428413919520">"ബഗ് റിപ്പോർട്ട് പുരോഗതിയിലാണ്"</string>
     <string name="bugreport_finished_title" msgid="2293711546892863898">"ബഗ് റിപ്പോർട്ട് ക്യാപ്‌ചർ ചെയ്‌തു"</string>
     <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"നിങ്ങളുടെ ബഗ് റിപ്പോർട്ട് പങ്കിടുന്നതിന് ഇടത്തേയ്‌ക്ക് സ്വൈപ്പുചെയ്യുക"</string>
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"നിങ്ങളുടെ ബഗ് റിപ്പോർട്ട് പങ്കിടാൻ സ്‌പർശിക്കുക"</string>
diff --git a/packages/Shell/res/values-mn-rMN/strings.xml b/packages/Shell/res/values-mn-rMN/strings.xml
index 71ad7f6..f7a9688 100644
--- a/packages/Shell/res/values-mn-rMN/strings.xml
+++ b/packages/Shell/res/values-mn-rMN/strings.xml
@@ -17,6 +17,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="3701846017049540910">"Шел"</string>
+    <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Алдааны тайлан үргэлжилж байна"</string>
     <string name="bugreport_finished_title" msgid="2293711546892863898">"Алдааны мэдээлэл хүлээн авав"</string>
     <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Өөрийн согог репортыг хуваалцахын тулд зүүн шудрана уу"</string>
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Та алдааны мэдэгдлийг хуваалцах бол хүрнэ үү"</string>
diff --git a/packages/Shell/res/values-mr-rIN/strings.xml b/packages/Shell/res/values-mr-rIN/strings.xml
index c32bb5b..e0db42b 100644
--- a/packages/Shell/res/values-mr-rIN/strings.xml
+++ b/packages/Shell/res/values-mr-rIN/strings.xml
@@ -17,6 +17,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="3701846017049540910">"शेल"</string>
+    <string name="bugreport_in_progress_title" msgid="6125357428413919520">"दोष अहवाल प्रगतीपथावर आहे"</string>
     <string name="bugreport_finished_title" msgid="2293711546892863898">"दोष अहवाल कॅप्‍चर केला"</string>
     <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"आपला दोष अहवाल सामायिक करण्यासाठी डावीकडे स्वाइप करा"</string>
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"आपला दोष अहवाल सामायिक करण्‍यासाठी स्‍पर्श करा"</string>
diff --git a/packages/Shell/res/values-ms-rMY/strings.xml b/packages/Shell/res/values-ms-rMY/strings.xml
index 852a9e4..bbbb42a 100644
--- a/packages/Shell/res/values-ms-rMY/strings.xml
+++ b/packages/Shell/res/values-ms-rMY/strings.xml
@@ -17,6 +17,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="3701846017049540910">"Shell"</string>
+    <!-- no translation found for bugreport_in_progress_title (6125357428413919520) -->
+    <skip />
     <string name="bugreport_finished_title" msgid="2293711546892863898">"Laporan pepijat telah ditangkap"</string>
     <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Leret ke kiri untuk berkongsi laporan pepijat anda"</string>
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Sentuh untuk berkongsi laporan pepijat anda"</string>
diff --git a/packages/Shell/res/values-my-rMM/strings.xml b/packages/Shell/res/values-my-rMM/strings.xml
index 4ba9037..d49dc15 100644
--- a/packages/Shell/res/values-my-rMM/strings.xml
+++ b/packages/Shell/res/values-my-rMM/strings.xml
@@ -17,6 +17,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="3701846017049540910">"အခွံ"</string>
+    <string name="bugreport_in_progress_title" msgid="6125357428413919520">"ချွတ်ယွင်းချက် အစီရင်ခံစာ ပြုလုပ်ဆဲ"</string>
     <string name="bugreport_finished_title" msgid="2293711546892863898">"အမှားအယွင်းမှတ်တမ်းကို အောင်မြင်စွာ သိမ်းဆည်းပြီး"</string>
     <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"သင်၏ ဘာဂ် အစီရင်ခံစာကို မျှပေးရန် ဘယ်ဘက်သို့ ပွတ်ဆွဲရန်"</string>
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"အမှားအယွင်း မှတ်တမ်းကို မျှဝေရန် ထိလိုက်ပါ"</string>
diff --git a/packages/Shell/res/values-nb/strings.xml b/packages/Shell/res/values-nb/strings.xml
index c543e49..d3aafdd 100644
--- a/packages/Shell/res/values-nb/strings.xml
+++ b/packages/Shell/res/values-nb/strings.xml
@@ -17,6 +17,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="3701846017049540910">"Kommandoliste"</string>
+    <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Feilrapport pågår"</string>
     <string name="bugreport_finished_title" msgid="2293711546892863898">"Feilrapporten er lagret"</string>
     <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Sveip til venstre for å dele feilrapporten din"</string>
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Trykk for å dele feilrapporten din"</string>
diff --git a/packages/Shell/res/values-ne-rNP/strings.xml b/packages/Shell/res/values-ne-rNP/strings.xml
index f57112a..fb81e55 100644
--- a/packages/Shell/res/values-ne-rNP/strings.xml
+++ b/packages/Shell/res/values-ne-rNP/strings.xml
@@ -17,6 +17,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="3701846017049540910">"सेल"</string>
+    <!-- no translation found for bugreport_in_progress_title (6125357428413919520) -->
+    <skip />
     <string name="bugreport_finished_title" msgid="2293711546892863898">"बग प्रतिवेदन समातियो"</string>
     <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"तपाईँको बग रिपोर्ट साझेदारी गर्न बायाँ स्वाइप गर्नुहोस्"</string>
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"तपाईंको बग रिपोर्ट साझेदारी गर्न छुनुहोस्"</string>
diff --git a/packages/Shell/res/values-nl/strings.xml b/packages/Shell/res/values-nl/strings.xml
index 1519454..a097fea 100644
--- a/packages/Shell/res/values-nl/strings.xml
+++ b/packages/Shell/res/values-nl/strings.xml
@@ -17,6 +17,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="3701846017049540910">"Shell"</string>
+    <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Bugrapport wordt uitgevoerd"</string>
     <string name="bugreport_finished_title" msgid="2293711546892863898">"Foutenrapport vastgelegd"</string>
     <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Veeg naar links om je bugmelding te delen"</string>
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Raak aan om je foutenrapport te delen"</string>
diff --git a/packages/Shell/res/values-pa-rIN/strings.xml b/packages/Shell/res/values-pa-rIN/strings.xml
index 3f59bb9..54e30d6 100644
--- a/packages/Shell/res/values-pa-rIN/strings.xml
+++ b/packages/Shell/res/values-pa-rIN/strings.xml
@@ -17,6 +17,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="3701846017049540910">"ਸ਼ੈਲ"</string>
+    <string name="bugreport_in_progress_title" msgid="6125357428413919520">"ਬੱਗ ਰਿਪੋਰਟ \'ਤੇ ਕਾਰਵਾਈ ਹੋ ਰਹੀ ਹੈ"</string>
     <string name="bugreport_finished_title" msgid="2293711546892863898">"ਬਗ ਰਿਪੋਰਟ ਕੈਪਚਰ ਕੀਤੀ"</string>
     <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"ਤੁਹਾਡੀ ਬਗ ਰਿਪੋਰਟ ਸ਼ੇਅਰ ਕਰਨ ਲਈ ਖੱਬੇ ਪਾਸੇ ਸਵਾਈਪ ਕਰੋ"</string>
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"ਆਪਣੀ ਬਗ ਰਿਪੋਰਟ ਸ਼ੇਅਰ ਕਰਨ ਲਈ ਛੋਹਵੋ"</string>
diff --git a/packages/Shell/res/values-pl/strings.xml b/packages/Shell/res/values-pl/strings.xml
index accc92e8..599f8c1 100644
--- a/packages/Shell/res/values-pl/strings.xml
+++ b/packages/Shell/res/values-pl/strings.xml
@@ -17,6 +17,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="3701846017049540910">"Powłoka"</string>
+    <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Trwa zgłaszanie błędu"</string>
     <string name="bugreport_finished_title" msgid="2293711546892863898">"Raport o błędach został zapisany"</string>
     <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Przesuń palcem w lewo, by udostępnić swoje zgłoszenie błędu"</string>
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Kliknij, by udostępnić raport o błędach"</string>
diff --git a/packages/Shell/res/values-pt-rBR/strings.xml b/packages/Shell/res/values-pt-rBR/strings.xml
index 7d4b0d3..a1a9ad1 100644
--- a/packages/Shell/res/values-pt-rBR/strings.xml
+++ b/packages/Shell/res/values-pt-rBR/strings.xml
@@ -17,6 +17,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="3701846017049540910">"Shell"</string>
+    <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Relatório de bugs em andamento"</string>
     <string name="bugreport_finished_title" msgid="2293711546892863898">"Relatório de bugs capturado"</string>
     <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Deslize para a esquerda para compartilhar seu relatório de bugs"</string>
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Toque para compartilhar seu relatório de bugs"</string>
diff --git a/packages/Shell/res/values-pt-rPT/strings.xml b/packages/Shell/res/values-pt-rPT/strings.xml
index 007d683..2e25f8c 100644
--- a/packages/Shell/res/values-pt-rPT/strings.xml
+++ b/packages/Shell/res/values-pt-rPT/strings.xml
@@ -17,6 +17,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="3701846017049540910">"Shell"</string>
+    <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Relatório de erro em curso"</string>
     <string name="bugreport_finished_title" msgid="2293711546892863898">"Relatório de erros capturado"</string>
     <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Deslizar rapidamente para a esquerda para partilhar o seu relatório de erros"</string>
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Toque para partilhar o relatório de erros"</string>
diff --git a/packages/Shell/res/values-pt/strings.xml b/packages/Shell/res/values-pt/strings.xml
index 7d4b0d3..a1a9ad1 100644
--- a/packages/Shell/res/values-pt/strings.xml
+++ b/packages/Shell/res/values-pt/strings.xml
@@ -17,6 +17,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="3701846017049540910">"Shell"</string>
+    <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Relatório de bugs em andamento"</string>
     <string name="bugreport_finished_title" msgid="2293711546892863898">"Relatório de bugs capturado"</string>
     <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Deslize para a esquerda para compartilhar seu relatório de bugs"</string>
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Toque para compartilhar seu relatório de bugs"</string>
diff --git a/packages/Shell/res/values-ro/strings.xml b/packages/Shell/res/values-ro/strings.xml
index e7395e1..d8a3b92 100644
--- a/packages/Shell/res/values-ro/strings.xml
+++ b/packages/Shell/res/values-ro/strings.xml
@@ -17,6 +17,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="3701846017049540910">"Shell"</string>
+    <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Raportul de eroare se creează"</string>
     <string name="bugreport_finished_title" msgid="2293711546892863898">"Raportul despre erori a fost creat"</string>
     <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Glisați la stânga pentru a trimite raportul de erori"</string>
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Atingeți pentru a permite accesul la raportul despre erori"</string>
diff --git a/packages/Shell/res/values-ru/strings.xml b/packages/Shell/res/values-ru/strings.xml
index c9276cf..436a590 100644
--- a/packages/Shell/res/values-ru/strings.xml
+++ b/packages/Shell/res/values-ru/strings.xml
@@ -17,6 +17,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="3701846017049540910">"Оболочка"</string>
+    <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Отправка сообщения об ошибке..."</string>
     <string name="bugreport_finished_title" msgid="2293711546892863898">"Отчет об ошибке сохранен"</string>
     <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Проведите влево, чтобы отправить отчет"</string>
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Нажмите, чтобы отправить отчет об ошибках"</string>
diff --git a/packages/Shell/res/values-si-rLK/strings.xml b/packages/Shell/res/values-si-rLK/strings.xml
index 937c1bc..a0bc5f6 100644
--- a/packages/Shell/res/values-si-rLK/strings.xml
+++ b/packages/Shell/res/values-si-rLK/strings.xml
@@ -17,6 +17,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="3701846017049540910">"ෂෙල්"</string>
+    <string name="bugreport_in_progress_title" msgid="6125357428413919520">"දෝෂය වාර්තා කිරීම සිදු කරමින් පවතී"</string>
     <string name="bugreport_finished_title" msgid="2293711546892863898">"දෝෂ වාර්තාව ලබාගන්නා ලදි"</string>
     <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"ඔබගේ දෝෂ වාර්තාව බෙදාගැනීමට වමට ස්වයිප් කරන්න"</string>
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"ඔබගේ දෝෂ වාර්තාව බෙදා ගැනීමට ස්පර්ශ කරන්න"</string>
diff --git a/packages/Shell/res/values-sk/strings.xml b/packages/Shell/res/values-sk/strings.xml
index cd52efc..e568946 100644
--- a/packages/Shell/res/values-sk/strings.xml
+++ b/packages/Shell/res/values-sk/strings.xml
@@ -17,6 +17,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="3701846017049540910">"Prostredie"</string>
+    <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Prebieha hlásenie chyby"</string>
     <string name="bugreport_finished_title" msgid="2293711546892863898">"Hlásenie o chybách bolo vytvorené"</string>
     <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Ak chcete hlásenie o chybe zdieľať, prejdite prstom doľava."</string>
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Hlásenie o chybách môžete zdielať klepnutím"</string>
diff --git a/packages/Shell/res/values-sl/strings.xml b/packages/Shell/res/values-sl/strings.xml
index 25553f2..6ac5ac8c 100644
--- a/packages/Shell/res/values-sl/strings.xml
+++ b/packages/Shell/res/values-sl/strings.xml
@@ -17,6 +17,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="3701846017049540910">"Lupina"</string>
+    <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Poročanje o napakah poteka"</string>
     <string name="bugreport_finished_title" msgid="2293711546892863898">"Poročilo o napaki je posneto"</string>
     <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Povlecite v levo, če želite poslati sporočilo o napaki"</string>
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Dotaknite se, če želite deliti sporočilo o napaki z drugimi"</string>
diff --git a/packages/Shell/res/values-sq-rAL/strings.xml b/packages/Shell/res/values-sq-rAL/strings.xml
index add53d8..54f2aad 100644
--- a/packages/Shell/res/values-sq-rAL/strings.xml
+++ b/packages/Shell/res/values-sq-rAL/strings.xml
@@ -17,6 +17,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="3701846017049540910">"Guaska"</string>
+    <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Raporti i gabimeve në kod është në progres"</string>
     <string name="bugreport_finished_title" msgid="2293711546892863898">"Raporti i defektit në kod u regjistrua"</string>
     <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Rrëshqit majtas për të ndarë raportin e defektit në kod"</string>
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Prek për të ndarë raportin e defektit në kod"</string>
diff --git a/packages/Shell/res/values-sr/strings.xml b/packages/Shell/res/values-sr/strings.xml
index bb60f3f..81cde00 100644
--- a/packages/Shell/res/values-sr/strings.xml
+++ b/packages/Shell/res/values-sr/strings.xml
@@ -17,6 +17,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="3701846017049540910">"Shell"</string>
+    <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Прављење извештаја о грешци је у току"</string>
     <string name="bugreport_finished_title" msgid="2293711546892863898">"Извештај о грешци је снимљен"</string>
     <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Превуците улево да бисте делили извештај о грешкама"</string>
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Додирните да бисте делили извештај о грешци"</string>
diff --git a/packages/Shell/res/values-sv/strings.xml b/packages/Shell/res/values-sv/strings.xml
index dd23e16..2287319 100644
--- a/packages/Shell/res/values-sv/strings.xml
+++ b/packages/Shell/res/values-sv/strings.xml
@@ -17,6 +17,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="3701846017049540910">"Skal"</string>
+    <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Felrapportering pågår"</string>
     <string name="bugreport_finished_title" msgid="2293711546892863898">"Felrapporten har skapats"</string>
     <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Svep åt vänster om du vill dela felrapporten"</string>
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Tryck om du vill dela felrapporten"</string>
diff --git a/packages/Shell/res/values-sw/strings.xml b/packages/Shell/res/values-sw/strings.xml
index 3773cd7..adb97dd 100644
--- a/packages/Shell/res/values-sw/strings.xml
+++ b/packages/Shell/res/values-sw/strings.xml
@@ -17,6 +17,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="3701846017049540910">"Ganda"</string>
+    <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Mchakato wa kuripoti hitilafu unaendelea"</string>
     <string name="bugreport_finished_title" msgid="2293711546892863898">"Ripoti ya hitilafu imenaswa"</string>
     <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Telezesha kidole kushoto ili ushiriki ripoti yako ya hitilafu"</string>
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Gusa ili ushiriki ripoti yako ya hitilafu"</string>
diff --git a/packages/Shell/res/values-ta-rIN/strings.xml b/packages/Shell/res/values-ta-rIN/strings.xml
index 244c929..15a6242 100644
--- a/packages/Shell/res/values-ta-rIN/strings.xml
+++ b/packages/Shell/res/values-ta-rIN/strings.xml
@@ -17,6 +17,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="3701846017049540910">"ஷெல்"</string>
+    <string name="bugreport_in_progress_title" msgid="6125357428413919520">"பிழை அறிக்கை செயலிலுள்ளது"</string>
     <string name="bugreport_finished_title" msgid="2293711546892863898">"பிழை அறிக்கைகள் படமெடுக்கப்பட்டன"</string>
     <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"பிழை அறிக்கையைப் பகிர இடது புறமாகத் தேய்க்கவும்"</string>
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"உங்கள் பிழை அறிக்கையைப் பகிர, தொடவும்"</string>
diff --git a/packages/Shell/res/values-te-rIN/strings.xml b/packages/Shell/res/values-te-rIN/strings.xml
index cd9b4b7..cd951a2 100644
--- a/packages/Shell/res/values-te-rIN/strings.xml
+++ b/packages/Shell/res/values-te-rIN/strings.xml
@@ -17,6 +17,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="3701846017049540910">"షెల్"</string>
+    <string name="bugreport_in_progress_title" msgid="6125357428413919520">"బగ్ నివేదిక ప్రోగ్రెస్‌లో ఉంది"</string>
     <string name="bugreport_finished_title" msgid="2293711546892863898">"బగ్ నివేదిక క్యాప్చర్ చేయబడింది"</string>
     <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"మీ బగ్ నివేదికను భాగస్వామ్యం చేయడానికి ఎడమవైపుకు స్వైప్ చేయండి"</string>
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"మీ బగ్ నివేదికను భాగస్వామ్యం చేయడానికి తాకండి"</string>
diff --git a/packages/Shell/res/values-th/strings.xml b/packages/Shell/res/values-th/strings.xml
index 35b7ec9..c61ffeb 100644
--- a/packages/Shell/res/values-th/strings.xml
+++ b/packages/Shell/res/values-th/strings.xml
@@ -17,6 +17,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="3701846017049540910">"Shell"</string>
+    <string name="bugreport_in_progress_title" msgid="6125357428413919520">"รายงานข้อบกพร่องอยู่ระหว่างดำเนินการ"</string>
     <string name="bugreport_finished_title" msgid="2293711546892863898">"จับภาพรายงานข้อบกพร่องแล้ว"</string>
     <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"กวาดไปทางซ้ายเพื่อแชร์รายงานข้อบกพร่อง"</string>
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"แตะเพื่อแชร์รายงานข้อบกพร่องของคุณ"</string>
diff --git a/packages/Shell/res/values-tl/strings.xml b/packages/Shell/res/values-tl/strings.xml
index 21df206..3e9a68d 100644
--- a/packages/Shell/res/values-tl/strings.xml
+++ b/packages/Shell/res/values-tl/strings.xml
@@ -17,6 +17,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="3701846017049540910">"Shell"</string>
+    <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Kasalukuyang ginagawa ang ulat ng bug"</string>
     <string name="bugreport_finished_title" msgid="2293711546892863898">"Na-capture ang ulat ng bug"</string>
     <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Mag-swipe pakaliwa upang ibahagi ang iyong ulat ng bug"</string>
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Pindutin upang ibahagi ang iyong ulat ng bug"</string>
diff --git a/packages/Shell/res/values-tr/strings.xml b/packages/Shell/res/values-tr/strings.xml
index fa8b82b..f90dae0 100644
--- a/packages/Shell/res/values-tr/strings.xml
+++ b/packages/Shell/res/values-tr/strings.xml
@@ -17,6 +17,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="3701846017049540910">"Kabuk"</string>
+    <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Hata raporu hazırlanıyor"</string>
     <string name="bugreport_finished_title" msgid="2293711546892863898">"Hata raporu kaydedildi"</string>
     <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Hata raporunuzu paylaşmak için hızlıca sola kaydırın"</string>
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Hata raporunuzu paylaşmak için dokunun"</string>
diff --git a/packages/Shell/res/values-uk/strings.xml b/packages/Shell/res/values-uk/strings.xml
index ba667f2..dac4fe0 100644
--- a/packages/Shell/res/values-uk/strings.xml
+++ b/packages/Shell/res/values-uk/strings.xml
@@ -17,6 +17,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="3701846017049540910">"Оболонка"</string>
+    <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Генерується повідомлення про помилку"</string>
     <string name="bugreport_finished_title" msgid="2293711546892863898">"Звіт про помилки створено"</string>
     <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Проведіть пальцем ліворуч, щоб надіслати звіт про помилки"</string>
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Торкніться, щоб надіслати звіт про помилки"</string>
diff --git a/packages/Shell/res/values-ur-rPK/strings.xml b/packages/Shell/res/values-ur-rPK/strings.xml
index 255aaf8..206f02a 100644
--- a/packages/Shell/res/values-ur-rPK/strings.xml
+++ b/packages/Shell/res/values-ur-rPK/strings.xml
@@ -17,6 +17,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="3701846017049540910">"شیل"</string>
+    <string name="bugreport_in_progress_title" msgid="6125357428413919520">"بگ رپورٹ پر پیشرفت جاری ہے"</string>
     <string name="bugreport_finished_title" msgid="2293711546892863898">"بَگ رپورٹ کیپچر کر لی گئی"</string>
     <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"اپنی بگ رپورٹ کا اشتراک کرنے کیلئے بائیں سوائپ کریں"</string>
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"اپنی بَگ رپورٹ کا اشتراک کرنے کیلئے ٹچ کریں"</string>
diff --git a/packages/Shell/res/values-uz-rUZ/strings.xml b/packages/Shell/res/values-uz-rUZ/strings.xml
index 998387e..0b8c1fb 100644
--- a/packages/Shell/res/values-uz-rUZ/strings.xml
+++ b/packages/Shell/res/values-uz-rUZ/strings.xml
@@ -17,6 +17,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="3701846017049540910">"Terminal"</string>
+    <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Xatoliklar hisoboti yuborilmoqda…"</string>
     <string name="bugreport_finished_title" msgid="2293711546892863898">"Xatolik hisobotini yozib olindi"</string>
     <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Xatolik hisobotini yuborish uchun barmog‘ingiz bilan chapga suring"</string>
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Xatolik hisobotini bo‘lishish uchun barmog‘ingizni tegizing."</string>
diff --git a/packages/Shell/res/values-vi/strings.xml b/packages/Shell/res/values-vi/strings.xml
index 5c11b13..9ef7cf9 100644
--- a/packages/Shell/res/values-vi/strings.xml
+++ b/packages/Shell/res/values-vi/strings.xml
@@ -17,6 +17,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="3701846017049540910">"Shell"</string>
+    <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Đang tiến hành báo cáo lỗi"</string>
     <string name="bugreport_finished_title" msgid="2293711546892863898">"Báo cáo lỗi đã được chụp"</string>
     <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Vuốt sang trái để chia sẻ báo cáo lỗi của bạn"</string>
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Chạm để chia sẻ báo cáo lỗi của bạn"</string>
diff --git a/packages/Shell/res/values-zh-rCN/strings.xml b/packages/Shell/res/values-zh-rCN/strings.xml
index 0a2f81b..0928bb3 100644
--- a/packages/Shell/res/values-zh-rCN/strings.xml
+++ b/packages/Shell/res/values-zh-rCN/strings.xml
@@ -17,6 +17,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="3701846017049540910">"Shell"</string>
+    <!-- no translation found for bugreport_in_progress_title (6125357428413919520) -->
+    <skip />
     <string name="bugreport_finished_title" msgid="2293711546892863898">"已抓取错误报告"</string>
     <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"向左滑动即可分享错误报告"</string>
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"触摸即可分享您的错误报告"</string>
diff --git a/packages/Shell/res/values-zh-rHK/strings.xml b/packages/Shell/res/values-zh-rHK/strings.xml
index 227d937..26a84c3 100644
--- a/packages/Shell/res/values-zh-rHK/strings.xml
+++ b/packages/Shell/res/values-zh-rHK/strings.xml
@@ -17,6 +17,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="3701846017049540910">"命令介面"</string>
+    <!-- no translation found for bugreport_in_progress_title (6125357428413919520) -->
+    <skip />
     <string name="bugreport_finished_title" msgid="2293711546892863898">"已擷取錯誤報告"</string>
     <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"向左滑動即可分享錯誤報告"</string>
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"輕觸即可分享您的錯誤報告"</string>
diff --git a/packages/Shell/res/values-zh-rTW/strings.xml b/packages/Shell/res/values-zh-rTW/strings.xml
index b9f8ee8..f79e58e 100644
--- a/packages/Shell/res/values-zh-rTW/strings.xml
+++ b/packages/Shell/res/values-zh-rTW/strings.xml
@@ -17,6 +17,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="3701846017049540910">"殼層"</string>
+    <!-- no translation found for bugreport_in_progress_title (6125357428413919520) -->
+    <skip />
     <string name="bugreport_finished_title" msgid="2293711546892863898">"已擷取錯誤報告"</string>
     <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"向左滑動即可分享錯誤報告"</string>
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"輕觸即可分享您的錯誤報告"</string>
diff --git a/packages/Shell/res/values-zu/strings.xml b/packages/Shell/res/values-zu/strings.xml
index b675858..2e209e1 100644
--- a/packages/Shell/res/values-zu/strings.xml
+++ b/packages/Shell/res/values-zu/strings.xml
@@ -17,6 +17,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="3701846017049540910">"I-Shell"</string>
+    <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Umbiko wesiphazamisi uyaqhubeka"</string>
     <string name="bugreport_finished_title" msgid="2293711546892863898">"Umbiko wesiphazamisi uthwetshuliwe"</string>
     <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Swayiphela kwesokunxele ukuze wabelane umbiko wesiphazamiso sakho"</string>
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Thinta ukuze wabelane ngombiko wakho wesiphazamisi"</string>
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index d0e91d2..f41f52c 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -41,6 +41,7 @@
 import android.accounts.Account;
 import android.accounts.AccountManager;
 import android.app.Notification;
+import android.app.Notification.Action;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.app.Service;
@@ -98,6 +99,7 @@
 
     static final String INTENT_BUGREPORT_STARTED = "android.intent.action.BUGREPORT_STARTED";
     static final String INTENT_BUGREPORT_FINISHED = "android.intent.action.BUGREPORT_FINISHED";
+    static final String INTENT_BUGREPORT_CANCEL = "android.intent.action.BUGREPORT_CANCEL";
 
     static final String EXTRA_BUGREPORT = "android.intent.extra.BUGREPORT";
     static final String EXTRA_SCREENSHOT = "android.intent.extra.SCREENSHOT";
@@ -116,7 +118,11 @@
     private static final long INACTIVITY_TIMEOUT = 3 * DateUtils.MINUTE_IN_MILLIS;
 
     /** System property used for monitoring progress. */
-    private static final String PROGRESS_PROPERTY_TEMPLATE = "dumpstate.%d.progress";
+    private static final String PROPERTY_TEMPLATE_PROGRESS = "dumpstate.%d.progress";
+
+    /** System property (and value) used for stop dumpstate. */
+    private static final String PROPERTY_CTL_STOP = "ctl.stop";
+    private static final String BUGREPORT_SERVICE = "bugreport";
 
     /** Managed dumpstate processes (keyed by pid) */
     private final SparseArray<BugreportInfo> mProcesses = new SparseArray<>();
@@ -226,6 +232,9 @@
                     }
                     stopProgress(pid, intent);
                     break;
+                case INTENT_BUGREPORT_CANCEL:
+                    cancel(pid);
+                    break;
                 default:
                     Log.w(TAG, "Unsupported intent: " + action);
             }
@@ -280,6 +289,14 @@
             nf.setMaximumFractionDigits(2);
             final String percentText = nf.format((double) info.progress / info.max);
 
+            final Intent cancelIntent = new Intent(context, BugreportReceiver.class);
+            cancelIntent.setAction(INTENT_BUGREPORT_CANCEL);
+            cancelIntent.putExtra(EXTRA_PID, info.pid);
+            final Action cancelAction = new Action.Builder(null,
+                    context.getString(com.android.internal.R.string.cancel),
+                    PendingIntent.getBroadcast(context, info.pid, cancelIntent,
+                            PendingIntent.FLAG_CANCEL_CURRENT)).build();
+
             final String title = context.getString(R.string.bugreport_in_progress_title);
             final Notification notification = new Notification.Builder(context)
                     .setSmallIcon(com.android.internal.R.drawable.stat_sys_adb)
@@ -288,10 +305,11 @@
                     .setContentText(info.name)
                     .setContentInfo(percentText)
                     .setProgress(info.max, info.progress, false)
-                    // TODO: .setOngoing(true) once it has a CANCEL action
+                    .setOngoing(true)
                     .setLocalOnly(true)
                     .setColor(context.getColor(
                             com.android.internal.R.color.system_notification_accent_color))
+                    .addAction(cancelAction)
                     .build();
 
             NotificationManager.from(context).notify(TAG, info.pid, notification);
@@ -319,6 +337,15 @@
         }
 
         /**
+         * Cancels a bugreport upon user's request.
+         */
+        private void cancel(int pid) {
+            Log.i(TAG, "Cancelling PID " + pid + " on user's request");
+            SystemProperties.set(PROPERTY_CTL_STOP, BUGREPORT_SERVICE);
+            stopProgress(pid, null);
+        }
+
+        /**
          * Poll {@link SystemProperties} to get the progress on each monitored process.
          */
         private void pollProgress() {
@@ -328,14 +355,8 @@
                 }
                 for (int i = 0; i < mProcesses.size(); i++) {
                     int pid = mProcesses.keyAt(i);
-                    String property = String.format(PROGRESS_PROPERTY_TEMPLATE, pid);
-                    int progress;
-                    try {
-                        progress = SystemProperties.getInt(property, 0);
-                    } catch (IllegalArgumentException e) {
-                        Log.v(TAG, "Could not read system property " + property, e);
-                        continue;
-                    }
+                    String property = String.format(PROPERTY_TEMPLATE_PROGRESS, pid);
+                    int progress = SystemProperties.getInt(property, 0);
                     if (progress == 0) {
                         Log.v(TAG, "System property " + property + " is not set yet");
                         continue;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java
index f32cfdc..4cc2b8d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java
@@ -222,7 +222,6 @@
         final ImageView icon = new ImageView(mContext);
         icon.setId(android.R.id.icon);
         icon.setScaleType(ScaleType.CENTER_INSIDE);
-        icon.setImageTintList(ColorStateList.valueOf(getContext().getColor(android.R.color.white)));
         return icon;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CustomTile.java
index b0d885a..d26e8d6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CustomTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CustomTile.java
@@ -23,6 +23,7 @@
 import android.content.ServiceConnection;
 import android.content.pm.PackageManager;
 import android.content.pm.ServiceInfo;
+import android.graphics.drawable.Drawable;
 import android.os.IBinder;
 import android.os.UserHandle;
 import android.service.quicksettings.IQSTileService;
@@ -132,7 +133,9 @@
     @Override
     protected void handleUpdateState(State state, Object arg) {
         state.visible = true;
-        state.icon = new DrawableIcon(mTile.getIcon().loadDrawable(mContext));
+        Drawable drawable = mTile.getIcon().loadDrawable(mContext);
+        drawable.setTint(mContext.getColor(android.R.color.white));
+        state.icon = new DrawableIcon(drawable);
         state.label = mTile.getLabel();
         if (mTile.getContentDescription() != null) {
             state.contentDescription = mTile.getContentDescription();
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
index 440ed6b1..cdfad18 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
@@ -69,7 +69,6 @@
     public final int smallestWidth;
 
     /** Misc **/
-    public boolean hasDockedTasks;
     public boolean useHardwareLayers;
     public boolean fakeShadows;
     public int svelteLevel;
@@ -112,7 +111,6 @@
         // settings or via multi window
         lockToAppEnabled = !ssp.hasFreeformWorkspaceSupport() &&
                 ssp.getSystemSetting(context, Settings.System.LOCK_TO_APP_ENABLED) != 0;
-        hasDockedTasks = ssp.hasDockedTask();
 
         // Recompute some values based on the given state, since we can not rely on the resource
         // system to get certain values.
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index 5616018..551f067 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -470,6 +470,25 @@
         if (event.dropTarget instanceof TaskStack.DockState) {
             final TaskStack.DockState dockState = (TaskStack.DockState) event.dropTarget;
 
+            // Remove the task after it is docked
+            if (event.taskView.isFocusedTask()) {
+                mTaskStackView.resetFocusedTask();
+            }
+            event.taskView.animate()
+                    .alpha(0f)
+                    .setDuration(150)
+                    .setInterpolator(mFastOutLinearInInterpolator)
+                    .setUpdateListener(null)
+                    .setListener(null)
+                    .withEndAction(new Runnable() {
+                        @Override
+                        public void run() {
+                            mTaskStackView.getStack().removeTask(event.task);
+                        }
+                    })
+                    .withLayer()
+                    .start();
+
             // Dock the task and launch it
             SystemServicesProxy ssp = Recents.getSystemServices();
             ssp.startTaskInDockedMode(event.task.key.id, dockState.createMode);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
index c55f383..2920295 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
@@ -27,6 +27,7 @@
 import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent;
 import com.android.systemui.recents.events.ui.dragndrop.DragStartInitializeDropTargetsEvent;
 import com.android.systemui.recents.misc.ReferenceCountedTrigger;
+import com.android.systemui.recents.misc.SystemServicesProxy;
 import com.android.systemui.recents.model.Task;
 import com.android.systemui.recents.model.TaskStack;
 
@@ -111,6 +112,7 @@
     /**** Events ****/
 
     public final void onBusEvent(DragStartEvent event) {
+        SystemServicesProxy ssp = Recents.getSystemServices();
         mRv.getParent().requestDisallowInterceptTouchEvent(true);
         mDragging = true;
         mDragTask = event.task;
@@ -127,7 +129,7 @@
         mTaskView.setTranslationY(y);
 
         RecentsConfiguration config = Recents.getConfiguration();
-        if (!config.hasDockedTasks) {
+        if (!ssp.hasDockedTask()) {
             // Add the dock state drop targets (these take priority)
             TaskStack.DockState[] dockStates = getDockStatesForCurrentOrientation();
             for (TaskStack.DockState dockState : dockStates) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index c02eaf8..0395e99 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -1559,7 +1559,13 @@
         int taskViewCount = taskViews.size();
         for (int i = taskViewCount - 1; i >= 0; i--) {
             TaskView tv = taskViews.get(i);
-            tv.animate().alpha(0f).setDuration(200).start();
+            tv.animate()
+                    .alpha(0f)
+                    .setDuration(200)
+                    .setUpdateListener(null)
+                    .setListener(null)
+                    .withLayer()
+                    .start();
         }
     }
 
@@ -1568,7 +1574,13 @@
         int taskViewCount = taskViews.size();
         for (int i = taskViewCount - 1; i >= 0; i--) {
             TaskView tv = taskViews.get(i);
-            tv.animate().alpha(1f).setDuration(200).start();
+            tv.animate()
+                    .alpha(1f)
+                    .setDuration(200)
+                    .setUpdateListener(null)
+                    .setListener(null)
+                    .withLayer()
+                    .start();
         }
     }
 
@@ -1580,7 +1592,9 @@
         Task task = tv.getTask();
 
         // Reset the previously focused task before it is removed from the stack
-        resetFocusedTask();
+        if (tv.isFocusedTask()) {
+            resetFocusedTask();
+        }
 
         // Announce for accessibility
         tv.announceForAccessibility(getContext().getString(
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
index 4a5956d..f2c89e6 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -395,6 +395,7 @@
                 .translationY(ctx.offscreenTranslationY)
                 .setStartDelay(0)
                 .setUpdateListener(null)
+                .setListener(null)
                 .setInterpolator(mFastOutLinearInInterpolator)
                 .setDuration(taskViewExitToHomeDuration)
                 .withEndAction(ctx.postAnimationTrigger.decrementAsRunnable())
@@ -443,6 +444,7 @@
                     .translationY(getTranslationY() + taskViewAffiliateGroupEnterOffset)
                     .setStartDelay(0)
                     .setUpdateListener(null)
+                    .setListener(null)
                     .setInterpolator(mFastOutLinearInInterpolator)
                     .setDuration(taskViewExitToAppDuration)
                     .start();
@@ -464,6 +466,7 @@
             .alpha(0f)
             .setStartDelay(delay)
             .setUpdateListener(null)
+            .setListener(null)
             .setInterpolator(mFastOutSlowInInterpolator)
             .setDuration(taskViewRemoveAnimDuration)
             .withEndAction(new Runnable() {
@@ -728,6 +731,8 @@
                     .scaleX(finalScale)
                     .scaleY(finalScale)
                     .setDuration(175)
+                    .setUpdateListener(null)
+                    .setListener(null)
                     .setInterpolator(mFastOutSlowInInterpolator)
                     .start();
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java
index 174ff33..6d43f9c 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java
@@ -122,6 +122,7 @@
             } else {
                 anim.setUpdateListener(null);
             }
+            anim.setListener(null);
             anim.setStartDelay(startDelay)
                     .setDuration(duration)
                     .setInterpolator(interp)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
index 6f8cd8c..da3cd54 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
@@ -130,6 +130,7 @@
     private final int mLowPriorityColor;
     private boolean mIsBelowSpeedBump;
     private FalsingManager mFalsingManager;
+    private boolean mTrackTouch;
 
     public ActivatableNotificationView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -175,15 +176,30 @@
     };
 
     @Override
-    public boolean onTouchEvent(MotionEvent event) {
-        if (mDimmed) {
+    public boolean dispatchTouchEvent(MotionEvent event) {
+        if (mDimmed && !mActivated) {
             return handleTouchEventDimmed(event);
         } else {
-            return super.onTouchEvent(event);
+            return super.dispatchTouchEvent(event);
         }
     }
 
     @Override
+    public boolean onTouchEvent(MotionEvent event) {
+        boolean result;
+        if (mDimmed && mActivated) {
+            result = handleTouchEventDimmed(event);
+        } else {
+            result = super.onTouchEvent(event);
+        }
+        if (mActivated && result && event.getAction() == MotionEvent.ACTION_UP) {
+            mFalsingManager.onNotificationDoubleTap();
+            removeCallbacks(mTapTimeoutRunnable);
+        }
+        return result;
+    }
+
+    @Override
     public void drawableHotspotChanged(float x, float y) {
         if (!mDimmed){
             mBackgroundNormal.drawableHotspotChanged(x, y);
@@ -206,14 +222,15 @@
             case MotionEvent.ACTION_DOWN:
                 mDownX = event.getX();
                 mDownY = event.getY();
+                mTrackTouch = true;
                 if (mDownY > getActualHeight()) {
-                    return false;
+                    mTrackTouch = false;
                 }
                 break;
             case MotionEvent.ACTION_MOVE:
                 if (!isWithinTouchSlop(event)) {
                     makeInactive(true /* animate */);
-                    return false;
+                    mTrackTouch = false;
                 }
                 break;
             case MotionEvent.ACTION_UP:
@@ -222,23 +239,23 @@
                         makeActive();
                         postDelayed(mTapTimeoutRunnable, DOUBLETAP_TIMEOUT_MS);
                     } else {
-                        mFalsingManager.onNotificationDoubleTap();
-                        boolean performed = performClick();
-                        if (performed) {
-                            removeCallbacks(mTapTimeoutRunnable);
+                        if (!performClick()) {
+                            return false;
                         }
                     }
                 } else {
                     makeInactive(true /* animate */);
+                    mTrackTouch = false;
                 }
                 break;
             case MotionEvent.ACTION_CANCEL:
                 makeInactive(true /* animate */);
+                mTrackTouch = false;
                 break;
             default:
                 break;
         }
-        return true;
+        return mTrackTouch;
     }
 
     private void makeActive() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 5d4c64e..3c7ff7f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -116,7 +116,8 @@
 
 public abstract class BaseStatusBar extends SystemUI implements
         CommandQueue.Callbacks, ActivatableNotificationView.OnActivatedListener,
-        ExpandableNotificationRow.ExpansionLogger, NotificationData.Environment {
+        ExpandableNotificationRow.ExpansionLogger, NotificationData.Environment,
+        ExpandableNotificationRow.OnExpandClickListener {
     public static final String TAG = "StatusBar";
     public static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
     public static final boolean MULTIUSER_DEBUG = false;
@@ -1298,9 +1299,14 @@
             // Since the number of notifications is determined based on the height of the view, we
             // need to update them.
             updateRowStates();
+            mStackScroller.onHeightChanged(null, false);
         }
     }
 
+    @Override
+    public void onExpandClicked(View clickedView, boolean nowExpanded) {
+    }
+
     protected class H extends Handler {
         public void handleMessage(Message m) {
             switch (m.what) {
@@ -1381,6 +1387,7 @@
                     parent, false);
             row.setExpansionLogger(this, entry.notification.getKey());
             row.setGroupManager(mGroupManager);
+            row.setOnExpandClickListener(this);
         }
 
         workAroundBadLayerDrawableOpacity(row);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index 81e20aa..7a94a58 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -104,14 +104,20 @@
     private boolean mIconAnimationRunning;
     private boolean mShowNoBackground;
     private ExpandableNotificationRow mNotificationParent;
+    private OnExpandClickListener mOnExpandClickListener;
     private OnClickListener mExpandClickListener = new OnClickListener() {
         @Override
         public void onClick(View v) {
             if (mGroupManager.isSummaryOfGroup(mStatusBarNotification)) {
                 mGroupManager.toggleGroupExpansion(mStatusBarNotification);
+                mOnExpandClickListener.onExpandClicked(ExpandableNotificationRow.this,
+                        mGroupManager.isGroupExpanded(mStatusBarNotification));
             } else {
-                setUserExpanded(!isExpanded());
+                boolean nowExpanded = !isExpanded();
+                setUserExpanded(nowExpanded);
                 notifyHeightChanged(true);
+                mOnExpandClickListener.onExpandClicked(ExpandableNotificationRow.this,
+                        nowExpanded);
             }
         }
     };
@@ -421,6 +427,10 @@
         mPrivateLayout.setSubTextVisible(visible);
     }
 
+    public void setOnExpandClickListener(OnExpandClickListener onExpandClickListener) {
+        mOnExpandClickListener = onExpandClickListener;
+    }
+
     public interface ExpansionLogger {
         public void logNotificationExpansion(String key, boolean userAction, boolean expanded);
     }
@@ -986,4 +996,8 @@
             mLogger.logNotificationExpansion(mLoggingKey, userAction, nowExpanded) ;
         }
     }
+
+    public interface OnExpandClickListener {
+        void onExpandClicked(View clickedView, boolean nowExpanded);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 6d13947..71904fa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -48,8 +48,8 @@
  */
 public class KeyguardIndicationController {
 
-    private static final String TAG = "KeyguardIndicationController";
-    private static final boolean DEBUG_CHARGING_CURRENT = false;
+    private static final String TAG = "KeyguardIndication";
+    private static final boolean DEBUG_CHARGING_SPEED = false;
 
     private static final int MSG_HIDE_TRANSIENT = 1;
     private static final int MSG_CLEAR_FP_MSG = 2;
@@ -72,7 +72,7 @@
     private boolean mPowerPluggedIn;
     private boolean mPowerCharged;
     private int mChargingSpeed;
-    private int mChargingCurrent;
+    private int mChargingWattage;
     private String mMessageToShowOnScreenOn;
 
     public KeyguardIndicationController(Context context, KeyguardIndicationTextView textView,
@@ -173,8 +173,8 @@
         }
         if (mPowerPluggedIn) {
             String indication = computePowerIndication();
-            if (DEBUG_CHARGING_CURRENT) {
-                indication += ",  " + (mChargingCurrent / 1000) + " mA";
+            if (DEBUG_CHARGING_SPEED) {
+                indication += ",  " + (mChargingWattage / 1000) + " mW";
             }
             return indication;
         }
@@ -231,7 +231,7 @@
                     || status.status == BatteryManager.BATTERY_STATUS_FULL;
             mPowerPluggedIn = status.isPluggedIn() && isChargingOrFull;
             mPowerCharged = status.isCharged();
-            mChargingCurrent = status.maxChargingCurrent;
+            mChargingWattage = status.maxChargingWattage;
             mChargingSpeed = status.getChargingSpeed(mSlowThreshold, mFastThreshold);
             updateIndication();
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
index bca6491..fb8086c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
@@ -431,6 +431,11 @@
      */
     private int calculateVisibleType() {
         boolean noExpandedChild = mExpandedChild == null;
+
+        if (!noExpandedChild && mContentHeight == mExpandedChild.getHeight()) {
+            return VISIBLE_TYPE_EXPANDED;
+        }
+
         if (mIsHeadsUp && mHeadsUpChild != null) {
             if (mContentHeight <= mHeadsUpChild.getHeight() || noExpandedChild) {
                 return VISIBLE_TYPE_HEADSUP;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
index 83dbde5..89edae3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
@@ -59,6 +59,7 @@
         public RemoteViews cachedBigContentView;
         public RemoteViews cachedHeadsUpContentView;
         public RemoteViews cachedPublicContentView;
+        public CharSequence remoteInputText;
 
         public Entry(StatusBarNotification n, StatusBarIconView ic) {
             this.key = n.getKey();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index c0e3ec1..73ee363 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -955,15 +955,7 @@
                 mQsTracking = false;
                 mTrackingPointer = -1;
                 trackMovement(event);
-                float fraction = getQsExpansionFraction();
-                if ((fraction != 0f || y >= mInitialTouchY)
-                        && (fraction != 1f || y <= mInitialTouchY)) {
-                    flingQsWithCurrentVelocity(y,
-                            event.getActionMasked() == MotionEvent.ACTION_CANCEL);
-                } else {
-                    logQsSwipeDown(y);
-                    mScrollYOverride = -1;
-                }
+                flingQsWithCurrentVelocity(y, event.getActionMasked() == MotionEvent.ACTION_CANCEL);
                 if (mVelocityTracker != null) {
                     mVelocityTracker.recycle();
                     mVelocityTracker = null;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 05660ec..685c4e5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -25,7 +25,6 @@
 import android.app.IActivityManager;
 import android.app.Notification;
 import android.app.PendingIntent;
-import android.app.RemoteInput;
 import android.app.StatusBarManager;
 import android.content.BroadcastReceiver;
 import android.content.ComponentCallbacks2;
@@ -3999,6 +3998,13 @@
         }
     }
 
+    @Override
+    public void onExpandClicked(View clickedView, boolean nowExpanded) {
+        if (mState == StatusBarState.KEYGUARD && nowExpanded) {
+            goToLockedShade(clickedView);
+        }
+    }
+
     /**
      * Goes back to the keyguard after hanging around in {@link StatusBarState#SHADE_LOCKED}.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
index acfe54d..22c0cb9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -148,6 +148,7 @@
 
     public void onDefocus() {
         mController.removeRemoteInput(mEntry);
+        mEntry.remoteInputText = mEditText.getText();
         setVisibility(INVISIBLE);
     }
 
@@ -171,6 +172,8 @@
         mEditText.setInnerFocusable(true);
         mController.addRemoteInput(mEntry);
         mEditText.mShowImeOnInputConnection = true;
+        mEditText.setText(mEntry.remoteInputText);
+        mEditText.setSelection(mEditText.getText().length());
         mEditText.requestFocus();
     }
 
@@ -216,8 +219,11 @@
 
         @Override
         public boolean onKeyPreIme(int keyCode, KeyEvent event) {
-            if (keyCode == KeyEvent.KEYCODE_BACK) {
+            if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) {
                 defocusIfNeeded();
+                final InputMethodManager imm = InputMethodManager.getInstance();
+                imm.hideSoftInputFromWindow(getWindowToken(), 0);
+                return true;
             }
             return super.onKeyPreIme(keyCode, event);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
index e00b890..b010761 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -342,7 +342,7 @@
 
     private void stopUserId(int id) {
         try {
-            ActivityManagerNative.getDefault().stopUser(id, null);
+            ActivityManagerNative.getDefault().stopUser(id, /* force= */ false, null);
         } catch (RemoteException e) {
             Log.e(TAG, "Couldn't stop user.", e);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index 185d32d..ae6b729 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -1867,7 +1867,7 @@
             }
             mNeedsAnimation = true;
         }
-        if (isHeadsUp(child)) {
+        if (isHeadsUp(child) && !mChangePositionInProgress) {
             mAddedHeadsUpChildren.add(child);
             mChildrenToAddAnimated.remove(child);
         }
diff --git a/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java b/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java
index 76b2346..2fe6648 100644
--- a/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java
+++ b/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 import android.content.DialogInterface;
 import android.net.IConnectivityManager;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
 import android.os.ServiceManager;
@@ -50,8 +51,8 @@
     private Handler mHandler;
 
     @Override
-    protected void onResume() {
-        super.onResume();
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
 
         if (getCallingPackage() != null) {
             Log.e(TAG, getCallingPackage() + " cannot start this activity");
@@ -108,11 +109,11 @@
     }
 
     @Override
-    protected void onPause() {
-        super.onPause();
+    protected void onDestroy() {
         if (!isFinishing()) {
             finish();
         }
+        super.onDestroy();
     }
 
     @Override
diff --git a/preloaded-classes b/preloaded-classes
index e44b25e..2301c41 100644
--- a/preloaded-classes
+++ b/preloaded-classes
@@ -3822,11 +3822,4 @@
 org.xmlpull.v1.XmlPullParser
 org.xmlpull.v1.XmlPullParserException
 org.xmlpull.v1.XmlSerializer
-<<<<<<< HEAD
 sun.misc.Unsafe
-=======
-<<<<<<< HEAD
-=======
-sun.misc.Unsafe
->>>>>>> 631d21f... Move StrictJarFile from libcore to framework
->>>>>>> 43ea2cc... DO NOT MERGE Move StrictJarFile from libcore to framework
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index 78a4e35..42dd9a8 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -70,6 +70,7 @@
 import android.content.res.TypedArray;
 import android.database.ContentObserver;
 import android.graphics.drawable.Drawable;
+import android.hardware.input.InputManagerInternal;
 import android.inputmethodservice.InputMethodService;
 import android.net.Uri;
 import android.os.Binder;
@@ -1930,6 +1931,16 @@
         }
     }
 
+    private void notifyInputMethodSubtypeChanged(final int userId,
+            @Nullable final InputMethodInfo inputMethodInfo,
+            @Nullable final InputMethodSubtype subtype) {
+        final InputManagerInternal inputManagerInternal =
+                LocalServices.getService(InputManagerInternal.class);
+        if (inputManagerInternal != null) {
+            inputManagerInternal.onInputMethodSubtypeChanged(userId, inputMethodInfo, subtype);
+        }
+    }
+
     /* package */ void setInputMethodLocked(String id, int subtypeId) {
         InputMethodInfo info = mMethodMap.get(id);
         if (info == null) {
@@ -1972,8 +1983,10 @@
                         mCurMethod.changeInputMethodSubtype(newSubtype);
                     } catch (RemoteException e) {
                         Slog.w(TAG, "Failed to call changeInputMethodSubtype");
+                        return;
                     }
                 }
+                notifyInputMethodSubtypeChanged(mSettings.getCurrentUserId(), info, newSubtype);
             }
             return;
         }
@@ -1999,6 +2012,9 @@
         } finally {
             Binder.restoreCallingIdentity(ident);
         }
+
+        notifyInputMethodSubtypeChanged(mSettings.getCurrentUserId(), info,
+                getCurrentInputMethodSubtypeLocked());
     }
 
     @Override
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 17b3d2a..2742c65 100755
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -30,10 +30,14 @@
 
 import android.app.ActivityThread;
 import android.app.AppOpsManager;
+import android.content.IIntentSender;
+import android.content.IntentSender;
 import android.os.Build;
+import android.os.Bundle;
 import android.os.DeadObjectException;
 import android.os.Handler;
 import android.os.Looper;
+import android.os.RemoteCallback;
 import android.os.SystemProperties;
 import android.os.TransactionTooLargeException;
 import android.util.ArrayMap;
@@ -300,7 +304,7 @@
     }
 
     ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
-            int callingPid, int callingUid, String callingPackage, int userId)
+            int callingPid, int callingUid, String callingPackage, final int userId)
             throws TransactionTooLargeException {
         if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "startService: " + service
                 + " type=" + resolvedType + " args=" + service.getExtras());
@@ -340,6 +344,18 @@
 
         NeededUriGrants neededGrants = mAm.checkGrantUriPermissionFromIntentLocked(
                 callingUid, r.packageName, service, service.getFlags(), null, r.userId);
+
+        // If permissions need a review before any of the app components can run,
+        // we do not start the service and launch a review activity if the calling app
+        // is in the foreground passing it a pending intent to start the service when
+        // review is completed.
+        if (Build.PERMISSIONS_REVIEW_REQUIRED) {
+            if (!requestStartTargetPermissionsReviewIfNeededLocked(r, callingPackage,
+                    callingUid, service, callerFg, userId)) {
+                return null;
+            }
+        }
+
         if (unscheduleServiceRestartLocked(r, callingUid, false)) {
             if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "START SERVICE WHILE RESTART PENDING: " + r);
         }
@@ -417,6 +433,50 @@
         return startServiceInnerLocked(smap, service, r, callerFg, addToStarting);
     }
 
+    private boolean requestStartTargetPermissionsReviewIfNeededLocked(ServiceRecord r,
+            String callingPackage, int callingUid, Intent service, boolean callerFg,
+            final int userId) {
+        if (mAm.getPackageManagerInternalLocked().isPermissionsReviewRequired(
+                r.packageName, r.userId)) {
+
+            // Show a permission review UI only for starting from a foreground app
+            if (!callerFg) {
+                Slog.w(TAG, "u" + r.userId + " Starting a service in package"
+                        + r.packageName + " requires a permissions review");
+                return false;
+            }
+
+            IIntentSender target = mAm.getIntentSenderLocked(
+                    ActivityManager.INTENT_SENDER_SERVICE, callingPackage,
+                    callingUid, userId, null, null, 0, new Intent[]{service},
+                    new String[]{service.resolveType(mAm.mContext.getContentResolver())},
+                    PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT
+                            | PendingIntent.FLAG_IMMUTABLE, null);
+
+            final Intent intent = new Intent(Intent.ACTION_REVIEW_PERMISSIONS);
+            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+                    | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+            intent.putExtra(Intent.EXTRA_PACKAGE_NAME, r.packageName);
+            intent.putExtra(Intent.EXTRA_INTENT, new IntentSender(target));
+
+            if (DEBUG_PERMISSIONS_REVIEW) {
+                Slog.i(TAG, "u" + r.userId + " Launching permission review for package "
+                        + r.packageName);
+            }
+
+            mAm.mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mAm.mContext.startActivityAsUser(intent, new UserHandle(userId));
+                }
+            });
+
+            return false;
+        }
+
+        return  true;
+    }
+
     ComponentName startServiceInnerLocked(ServiceMap smap, Intent service, ServiceRecord r,
             boolean callerFg, boolean addToStarting) throws TransactionTooLargeException {
         ProcessStats.ServiceState stracker = r.getTracker();
@@ -427,7 +487,7 @@
         synchronized (r.stats.getBatteryStats()) {
             r.stats.startRunningLocked();
         }
-        String error = bringUpServiceLocked(r, service.getFlags(), callerFg, false);
+        String error = bringUpServiceLocked(r, service.getFlags(), callerFg, false, false);
         if (error != null) {
             return new ComponentName("!!", error);
         }
@@ -721,8 +781,8 @@
     }
 
     int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service,
-            String resolvedType, IServiceConnection connection, int flags,
-            String callingPackage, int userId) throws TransactionTooLargeException {
+            String resolvedType, final IServiceConnection connection, int flags,
+            String callingPackage, final int userId) throws TransactionTooLargeException {
         if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "bindService: " + service
                 + " type=" + resolvedType + " conn=" + connection.asBinder()
                 + " flags=0x" + Integer.toHexString(flags));
@@ -783,6 +843,83 @@
         }
         ServiceRecord s = res.record;
 
+        boolean permissionsReviewRequired = false;
+
+        // If permissions need a review before any of the app components can run,
+        // we schedule binding to the service but do not start its process, then
+        // we launch a review activity to which is passed a callback to invoke
+        // when done to start the bound service's process to completing the binding.
+        if (Build.PERMISSIONS_REVIEW_REQUIRED) {
+            if (mAm.getPackageManagerInternalLocked().isPermissionsReviewRequired(
+                    s.packageName, s.userId)) {
+
+                permissionsReviewRequired = true;
+
+                // Show a permission review UI only for binding from a foreground app
+                if (!callerFg) {
+                    Slog.w(TAG, "u" + s.userId + " Binding to a service in package"
+                            + s.packageName + " requires a permissions review");
+                    return 0;
+                }
+
+                final ServiceRecord serviceRecord = s;
+                final Intent serviceIntent = service;
+
+                RemoteCallback callback = new RemoteCallback(
+                        new RemoteCallback.OnResultListener() {
+                    @Override
+                    public void onResult(Bundle result) {
+                        synchronized(mAm) {
+                            final long identity = Binder.clearCallingIdentity();
+                            try {
+                                if (!mPendingServices.contains(serviceRecord)) {
+                                    return;
+                                }
+                                // If there is still a pending record, then the service
+                                // binding request is still valid, so hook them up. We
+                                // proceed only if the caller cleared the review requirement
+                                // otherwise we unbind because the user didn't approve.
+                                if (!mAm.getPackageManagerInternalLocked()
+                                        .isPermissionsReviewRequired(
+                                                serviceRecord.packageName,
+                                                serviceRecord.userId)) {
+                                    try {
+                                        bringUpServiceLocked(serviceRecord,
+                                                serviceIntent.getFlags(),
+                                                callerFg, false, false);
+                                    } catch (RemoteException e) {
+                                        /* ignore - local call */
+                                    }
+                                } else {
+                                    unbindServiceLocked(connection);
+                                }
+                            } finally {
+                                Binder.restoreCallingIdentity(identity);
+                            }
+                        }
+                    }
+                });
+
+                final Intent intent = new Intent(Intent.ACTION_REVIEW_PERMISSIONS);
+                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+                        | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+                intent.putExtra(Intent.EXTRA_PACKAGE_NAME, s.packageName);
+                intent.putExtra(Intent.EXTRA_REMOTE_CALLBACK, callback);
+
+                if (DEBUG_PERMISSIONS_REVIEW) {
+                    Slog.i(TAG, "u" + s.userId + " Launching permission review for package "
+                            + s.packageName);
+                }
+
+                mAm.mHandler.post(new Runnable() {
+                    @Override
+                    public void run() {
+                        mAm.mContext.startActivityAsUser(intent, new UserHandle(userId));
+                    }
+                });
+            }
+        }
+
         final long origId = Binder.clearCallingIdentity();
 
         try {
@@ -840,7 +977,8 @@
 
             if ((flags&Context.BIND_AUTO_CREATE) != 0) {
                 s.lastActivity = SystemClock.uptimeMillis();
-                if (bringUpServiceLocked(s, service.getFlags(), callerFg, false) != null) {
+                if (bringUpServiceLocked(s, service.getFlags(), callerFg, false,
+                        permissionsReviewRequired) != null) {
                     return 0;
                 }
             }
@@ -890,6 +1028,10 @@
         return 1;
     }
 
+    private void foo() {
+
+    }
+
     void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) {
         final long origId = Binder.clearCallingIdentity();
         try {
@@ -1366,7 +1508,7 @@
             return;
         }
         try {
-            bringUpServiceLocked(r, r.intent.getIntent().getFlags(), r.createdFromFg, true);
+            bringUpServiceLocked(r, r.intent.getIntent().getFlags(), r.createdFromFg, true, false);
         } catch (TransactionTooLargeException e) {
             // Ignore, it's been logged and nothing upstack cares.
         }
@@ -1410,8 +1552,9 @@
         }
     }
 
-    private final String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg,
-            boolean whileRestarting) throws TransactionTooLargeException {
+    private String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg,
+            boolean whileRestarting, boolean permissionsReviewRequired)
+            throws TransactionTooLargeException {
         //Slog.i(TAG, "Bring up service:");
         //r.dump("  ");
 
@@ -1497,7 +1640,7 @@
 
         // Not running -- get it started, and enqueue this service record
         // to be executed when the app comes up.
-        if (app == null) {
+        if (app == null && !permissionsReviewRequired) {
             if ((app=mAm.startProcessLocked(procName, r.appInfo, true, intentFlags,
                     "service", r.name, false, isolated, false)) == null) {
                 String msg = "Unable to launch app "
@@ -1920,6 +2063,9 @@
                 }
             }
 
+            // If unbound while waiting to start, remove the pending service
+            mPendingServices.remove(s);
+
             if ((c.flags&Context.BIND_AUTO_CREATE) != 0) {
                 boolean hasAutoCreate = s.hasAutoCreateConnections();
                 if (!hasAutoCreate) {
@@ -2962,5 +3108,4 @@
             }
         }
     }
-
 }
diff --git a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
index 16c959f..4f0d4d9 100644
--- a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
+++ b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
@@ -88,6 +88,7 @@
     static final boolean DEBUG_VISIBILITY = DEBUG_ALL || false;
     static final boolean DEBUG_VISIBLE_BEHIND = DEBUG_ALL_ACTIVITIES || false;
     static final boolean DEBUG_USAGE_STATS = DEBUG_ALL || false;
+    static final boolean DEBUG_PERMISSIONS_REVIEW = DEBUG_ALL || false;
 
     static final String POSTFIX_ADD_REMOVE = (APPEND_CATEGORY_NAME) ? "_AddRemove" : "";
     static final String POSTFIX_APP = (APPEND_CATEGORY_NAME) ? "_App" : "";
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 99470c8..ef623eb 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -16,9 +16,65 @@
 
 package com.android.server.am;
 
+import static android.Manifest.permission.INTERACT_ACROSS_USERS;
+import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static com.android.internal.util.XmlUtils.readBooleanAttribute;
+import static com.android.internal.util.XmlUtils.readIntAttribute;
+import static com.android.internal.util.XmlUtils.readLongAttribute;
+import static com.android.internal.util.XmlUtils.writeBooleanAttribute;
+import static com.android.internal.util.XmlUtils.writeIntAttribute;
+import static com.android.internal.util.XmlUtils.writeLongAttribute;
+import static com.android.server.Watchdog.NATIVE_STACKS_OF_INTEREST;
+import static com.android.server.am.ActivityManagerDebugConfig.*;
+import static com.android.server.am.TaskRecord.INVALID_TASK_ID;
+import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_DONT_LOCK;
+import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE_PRIV;
+import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_PINNABLE;
+import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
+import static org.xmlpull.v1.XmlPullParser.START_TAG;
+
 import com.google.android.collect.Lists;
 import com.google.android.collect.Maps;
 
+import android.Manifest;
+import android.app.AppOpsManager;
+import android.app.ApplicationThreadNative;
+import android.app.BroadcastOptions;
+import android.app.IActivityContainer;
+import android.app.IActivityContainerCallback;
+import android.app.IAppTask;
+import android.app.ITaskStackListener;
+import android.app.ProfilerInfo;
+import android.app.assist.AssistContent;
+import android.app.assist.AssistStructure;
+import android.app.usage.UsageEvents;
+import android.app.usage.UsageStatsManagerInternal;
+import android.appwidget.AppWidgetManager;
+import android.content.pm.PackageManagerInternal;
+import android.content.pm.PermissionInfo;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.os.BatteryStats;
+import android.os.PersistableBundle;
+import android.os.PowerManager;
+import android.os.Trace;
+import android.os.TransactionTooLargeException;
+import android.os.WorkSource;
+import android.os.storage.IMountService;
+import android.os.storage.MountServiceInternal;
+import android.os.storage.StorageManager;
+import android.service.voice.IVoiceInteractionSession;
+import android.service.voice.VoiceInteractionSession;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.DebugUtils;
+import android.util.SparseIntArray;
+import android.view.Display;
+
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.app.AssistUtils;
@@ -1457,6 +1513,8 @@
     final MainHandler mHandler;
     final UiHandler mUiHandler;
 
+    PackageManagerInternal mPackageManagerInt;
+
     final class UiHandler extends Handler {
         public UiHandler() {
             super(com.android.server.UiThread.get().getLooper(), null, true);
@@ -2713,57 +2771,60 @@
     }
 
     final void setFocusedActivityLocked(ActivityRecord r, String reason) {
-        if (r != null && mFocusedActivity != r) {
-            if (DEBUG_FOCUS) Slog.d(TAG_FOCUS, "setFocusedActivityLocked: r=" + r);
-            ActivityRecord last = mFocusedActivity;
-            mFocusedActivity = r;
-            if (r.task.taskType != ActivityRecord.HOME_ACTIVITY_TYPE
-                    && r.task.taskType != ActivityRecord.RECENTS_ACTIVITY_TYPE) {
-                if (mCurAppTimeTracker != r.appTimeTracker) {
-                    // We are switching app tracking.  Complete the current one.
-                    if (mCurAppTimeTracker != null) {
-                        mCurAppTimeTracker.stop();
-                        mHandler.obtainMessage(REPORT_TIME_TRACKER_MSG,
-                                mCurAppTimeTracker).sendToTarget();
-                        mStackSupervisor.clearOtherAppTimeTrackers(r.appTimeTracker);
-                        mCurAppTimeTracker = null;
-                    }
-                    if (r.appTimeTracker != null) {
-                        mCurAppTimeTracker = r.appTimeTracker;
-                        startTimeTrackingFocusedActivityLocked();
-                    }
-                } else {
+        if (r == null || mFocusedActivity == r) {
+            return;
+        }
+
+        if (DEBUG_FOCUS) Slog.d(TAG_FOCUS, "setFocusedActivityLocked: r=" + r);
+        final ActivityRecord last = mFocusedActivity;
+        mFocusedActivity = r;
+        if (r.task.isApplicationTask()) {
+            if (mCurAppTimeTracker != r.appTimeTracker) {
+                // We are switching app tracking.  Complete the current one.
+                if (mCurAppTimeTracker != null) {
+                    mCurAppTimeTracker.stop();
+                    mHandler.obtainMessage(
+                            REPORT_TIME_TRACKER_MSG, mCurAppTimeTracker).sendToTarget();
+                    mStackSupervisor.clearOtherAppTimeTrackers(r.appTimeTracker);
+                    mCurAppTimeTracker = null;
+                }
+                if (r.appTimeTracker != null) {
+                    mCurAppTimeTracker = r.appTimeTracker;
                     startTimeTrackingFocusedActivityLocked();
                 }
             } else {
-                r.appTimeTracker = null;
+                startTimeTrackingFocusedActivityLocked();
             }
-            if (r.task != null && r.task.voiceInteractor != null) {
-                startRunningVoiceLocked(r.task.voiceSession, r.info.applicationInfo.uid);
-            } else {
-                finishRunningVoiceLocked();
-                if (last != null && last.task.voiceSession != null) {
-                    // We had been in a voice interaction session, but now focused has
-                    // move to something different.  Just finish the session, we can't
-                    // return to it and retain the proper state and synchronization with
-                    // the voice interaction service.
-                    finishVoiceTask(last.task.voiceSession);
-                }
-            }
-            if (mStackSupervisor.setFocusedStack(r, reason + " setFocusedActivity")) {
-                mWindowManager.setFocusedApp(r.appToken, true);
-            }
-            applyUpdateLockStateLocked(r);
-            if (mFocusedActivity.userId != mLastFocusedUserId) {
-                mHandler.removeMessages(FOREGROUND_PROFILE_CHANGED_MSG);
-                mHandler.sendMessage(mHandler.obtainMessage(FOREGROUND_PROFILE_CHANGED_MSG,
-                        mFocusedActivity.userId, 0));
-                mLastFocusedUserId = mFocusedActivity.userId;
+        } else {
+            r.appTimeTracker = null;
+        }
+        if (r.task.voiceInteractor != null) {
+            startRunningVoiceLocked(r.task.voiceSession, r.info.applicationInfo.uid);
+        } else {
+            finishRunningVoiceLocked();
+            if (last != null && last.task.voiceSession != null) {
+                // We had been in a voice interaction session, but now focused has
+                // move to something different.  Just finish the session, we can't
+                // return to it and retain the proper state and synchronization with
+                // the voice interaction service.
+                finishVoiceTask(last.task.voiceSession);
             }
         }
-        EventLog.writeEvent(EventLogTags.AM_FOCUSED_ACTIVITY,
+        if (mStackSupervisor.setFocusedStack(r, reason + " setFocusedActivity")) {
+            mWindowManager.setFocusedApp(r.appToken, true);
+        }
+        applyUpdateLockStateLocked(r);
+        if (mFocusedActivity.userId != mLastFocusedUserId) {
+            mHandler.removeMessages(FOREGROUND_PROFILE_CHANGED_MSG);
+            mHandler.obtainMessage(
+                    FOREGROUND_PROFILE_CHANGED_MSG, mFocusedActivity.userId, 0).sendToTarget();
+            mLastFocusedUserId = mFocusedActivity.userId;
+        }
+
+        EventLogTags.writeAmFocusedActivity(
                 mFocusedActivity == null ? -1 : mFocusedActivity.userId,
-                mFocusedActivity == null ? "NULL" : mFocusedActivity.shortComponentName);
+                mFocusedActivity == null ? "NULL" : mFocusedActivity.shortComponentName,
+                reason);
     }
 
     final void clearFocusedActivity(ActivityRecord r) {
@@ -2779,7 +2840,7 @@
                 }
             }
             mFocusedActivity = null;
-            EventLog.writeEvent(EventLogTags.AM_FOCUSED_ACTIVITY, -1, "NULL");
+            EventLogTags.writeAmFocusedActivity(-1, "NULL", "clearFocusedActivity");
         }
     }
 
@@ -4397,8 +4458,10 @@
             if (r == null) {
                 return;
             }
-            if (r.task != null && r.task.mResizeable) {
-                // Fixed screen orientation isn't supported with resizeable activities.
+            TaskRecord task = r.task;
+            if (task != null && (!task.mFullscreen || !task.stack.mFullscreen)) {
+                // Fixed screen orientation isn't supported when activities aren't in full screen
+                // mode.
                 return;
             }
             final long origId = Binder.clearCallingIdentity();
@@ -5326,6 +5389,11 @@
                     removeUriPermissionsForPackageLocked(packageName, userId, true);
                 }
 
+                // Remove all zen rules created by this package; revoke it's zen access.
+                INotificationManager inm = NotificationManager.getService();
+                inm.removeAutomaticZenRules(packageName);
+                inm.setNotificationPolicyAccessGranted(packageName, false);
+
                 Intent intent = new Intent(Intent.ACTION_PACKAGE_DATA_CLEARED,
                         Uri.fromParts("package", packageName, null));
                 intent.putExtra(Intent.EXTRA_UID, pkgUid);
@@ -6009,6 +6077,8 @@
                         "No more processes in " + old.uidRecord);
                 enqueueUidChangeLocked(old.uidRecord, -1, UidRecord.CHANGE_GONE);
                 mActiveUids.remove(uid);
+                mBatteryStatsService.noteUidProcessState(uid,
+                        ActivityManager.PROCESS_STATE_NONEXISTENT);
             }
             old.uidRecord = null;
         }
@@ -6033,6 +6103,7 @@
             if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
                     "Creating new process uid: " + uidRec);
             mActiveUids.put(proc.uid, uidRec);
+            mBatteryStatsService.noteUidProcessState(uidRec.uid, uidRec.curProcState);
             enqueueUidChangeLocked(uidRec, -1, UidRecord.CHANGE_ACTIVE);
         }
         proc.uidRecord = uidRec;
@@ -7146,22 +7217,40 @@
 
         @Override
         public void getProcessStatesFromPids(/*in*/ int[] pids, /*out*/ int[] states) {
-            mActivityManagerService.getProcessStatesForPIDs(/*in*/ pids, /*out*/ states);
+            mActivityManagerService.getProcessStatesAndOomScoresForPIDs(
+                    /*in*/ pids, /*out*/ states, null);
+        }
+
+        @Override
+        public void getProcessStatesAndOomScoresFromPids(
+                /*in*/ int[] pids, /*out*/ int[] states, /*out*/ int[] scores) {
+            mActivityManagerService.getProcessStatesAndOomScoresForPIDs(
+                    /*in*/ pids, /*out*/ states, /*out*/ scores);
         }
     }
 
     /**
      * For each PID in the given input array, write the current process state
-     * for that process into the output array, or -1 to indicate that no
-     * process with the given PID exists.
+     * for that process into the states array, or -1 to indicate that no
+     * process with the given PID exists. If scores array is provided, write
+     * the oom score for the process into the scores array, with INVALID_ADJ
+     * indicating the PID doesn't exist.
      */
-    public void getProcessStatesForPIDs(/*in*/ int[] pids, /*out*/ int[] states) {
+    public void getProcessStatesAndOomScoresForPIDs(
+            /*in*/ int[] pids, /*out*/ int[] states, /*out*/ int[] scores) {
+        if (scores != null) {
+            enforceCallingPermission(android.Manifest.permission.GET_PROCESS_STATE_AND_OOM_SCORE,
+                    "getProcessStatesAndOomScoresForPIDs()");
+        }
+
         if (pids == null) {
             throw new NullPointerException("pids");
         } else if (states == null) {
             throw new NullPointerException("states");
         } else if (pids.length != states.length) {
-            throw new IllegalArgumentException("input and output arrays have different lengths!");
+            throw new IllegalArgumentException("pids and states arrays have different lengths!");
+        } else if (scores != null && pids.length != scores.length) {
+            throw new IllegalArgumentException("pids and scores arrays have different lengths!");
         }
 
         synchronized (mPidsSelfLocked) {
@@ -7169,6 +7258,9 @@
                 ProcessRecord pr = mPidsSelfLocked.get(pids[i]);
                 states[i] = (pr == null) ? ActivityManager.PROCESS_STATE_NONEXISTENT :
                         pr.curProcState;
+                if (scores != null) {
+                    scores[i] = (pr == null) ? ProcessList.INVALID_ADJ : pr.curAdj;
+                }
             }
         }
     }
@@ -9942,14 +10034,6 @@
                 }
 
                 if (cpr.proc != null) {
-                    if (false) {
-                        if (cpr.name.flattenToShortString().equals(
-                                "com.android.providers.calendar/.CalendarProvider2")) {
-                            Slog.v(TAG, "****************** KILLING "
-                                + cpr.name.flattenToShortString());
-                            Process.killProcess(cpr.proc.pid);
-                        }
-                    }
                     checkTime(startTime, "getContentProviderImpl: before updateOomAdj");
                     boolean success = updateOomAdjLocked(cpr.proc);
                     maybeUpdateProviderUsageStatsLocked(r, cpr.info.packageName, name);
@@ -10042,6 +10126,16 @@
                 final boolean firstClass = cpr == null;
                 if (firstClass) {
                     final long ident = Binder.clearCallingIdentity();
+
+                    // If permissions need a review before any of the app components can run,
+                    // we return no provider and launch a review activity if the calling app
+                    // is in the foreground.
+                    if (Build.PERMISSIONS_REVIEW_REQUIRED) {
+                        if (!requestTargetProviderPermissionsReviewIfNeededLocked(cpi, r, userId)) {
+                            return null;
+                        }
+                    }
+
                     try {
                         checkTime(startTime, "getContentProviderImpl: before getApplicationInfo");
                         ApplicationInfo ai =
@@ -10194,6 +10288,52 @@
         return cpr != null ? cpr.newHolder(conn) : null;
     }
 
+    private boolean requestTargetProviderPermissionsReviewIfNeededLocked(ProviderInfo cpi,
+            ProcessRecord r, final int userId) {
+        if (getPackageManagerInternalLocked().isPermissionsReviewRequired(
+                cpi.packageName, r.userId)) {
+
+            final boolean callerForeground = r != null ? r.setSchedGroup
+                    != Process.THREAD_GROUP_BG_NONINTERACTIVE : true;
+
+            // Show a permission review UI only for starting from a foreground app
+            if (!callerForeground) {
+                Slog.w(TAG, "u" + r.userId + " Instantiating a provider in package"
+                        + cpi.packageName + " requires a permissions review");
+                return false;
+            }
+
+            final Intent intent = new Intent(Intent.ACTION_REVIEW_PERMISSIONS);
+            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+                    | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+            intent.putExtra(Intent.EXTRA_PACKAGE_NAME, cpi.packageName);
+
+            if (DEBUG_PERMISSIONS_REVIEW) {
+                Slog.i(TAG, "u" + r.userId + " Launching permission review "
+                        + "for package " + cpi.packageName);
+            }
+
+            final UserHandle userHandle = new UserHandle(userId);
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mContext.startActivityAsUser(intent, userHandle);
+                }
+            });
+
+            return false;
+        }
+
+        return true;
+    }
+
+    PackageManagerInternal getPackageManagerInternalLocked() {
+        if (mPackageManagerInt == null) {
+            mPackageManagerInt = LocalServices.getService(PackageManagerInternal.class);
+        }
+        return mPackageManagerInt;
+    }
+
     @Override
     public final ContentProviderHolder getContentProvider(
             IApplicationThread caller, String name, int userId, boolean stable) {
@@ -10897,7 +11037,7 @@
     public void stopAppSwitches() {
         if (checkCallingPermission(android.Manifest.permission.STOP_APP_SWITCHES)
                 != PackageManager.PERMISSION_GRANTED) {
-            throw new SecurityException("Requires permission "
+            throw new SecurityException("viewquires permission "
                     + android.Manifest.permission.STOP_APP_SWITCHES);
         }
 
@@ -17759,7 +17899,7 @@
      * @param userId is only used when persistent parameter is set to true to persist configuration
      *               for that particular user
      */
-    boolean updateConfigurationLocked(Configuration values,
+    private boolean updateConfigurationLocked(Configuration values,
             ActivityRecord starting, boolean initLocale, boolean persistent, int userId) {
         int changes = 0;
 
@@ -19370,10 +19510,6 @@
             if (proc.baseProcessTracker != null) {
                 proc.baseProcessTracker.setState(proc.repProcState, memFactor, now, proc.pkgList);
             }
-            if (proc.repProcState >= 0) {
-                mBatteryStatsService.noteProcessState(proc.processName, proc.info.uid,
-                        proc.repProcState);
-            }
         }
     }
 
@@ -19873,6 +20009,7 @@
                 }
                 uidRec.setProcState = uidRec.curProcState;
                 enqueueUidChangeLocked(uidRec, -1, uidChange);
+                mBatteryStatsService.noteUidProcessState(uidRec.uid, uidRec.curProcState);
             }
         }
 
@@ -20300,8 +20437,8 @@
     }
 
     @Override
-    public int stopUser(final int userId, final IStopUserCallback callback) {
-        return mUserController.stopUser(userId, callback);
+    public int stopUser(final int userId, boolean force, final IStopUserCallback callback) {
+        return mUserController.stopUser(userId, force, callback);
     }
 
     void onUserRemovedLocked(int userId) {
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index da2c8c5c..02a372a 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -54,7 +54,6 @@
 import android.app.ResultInfo;
 import android.app.ActivityManager.RunningTaskInfo;
 import android.content.ComponentName;
-import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager;
@@ -73,7 +72,6 @@
 import android.os.SystemClock;
 import android.os.Trace;
 import android.os.UserHandle;
-import android.provider.Settings;
 import android.service.voice.IVoiceInteractionSession;
 import android.util.EventLog;
 import android.util.Slog;
@@ -2827,41 +2825,43 @@
     }
 
     private void adjustFocusedActivityLocked(ActivityRecord r, String reason) {
-        if (mStackSupervisor.isFocusedStack(this) && mService.mFocusedActivity == r) {
-            ActivityRecord next = topRunningActivityLocked();
-            final String myReason = reason + " adjustFocus";
-            if (next != r) {
-                if (next != null && StackId.keepFocusInStackIfPossible(mStackId)) {
-                    // For freeform, docked, and pinned stacks we always keep the focus within the
-                    // stack as long as there is a running activity in the stack that we can adjust
-                    // focus to.
-                    mService.setFocusedActivityLocked(next, myReason);
-                    return;
-                } else {
-                    final TaskRecord task = r.task;
-                    if (r.frontOfTask && task == topTask() && task.isOverHomeStack()) {
-                        // For non-fullscreen stack, we want to move the focus to the next visible
-                        // stack to prevent the home screen from moving to the top and obscuring
-                        // other visible stacks.
-                        if (!mFullscreen
-                                && adjustFocusToNextVisibleStackLocked(null, myReason)) {
-                            return;
-                        }
-                        // Move the home stack to the top if this stack is fullscreen or there is no
-                        // other visible stack.
-                        if (mStackSupervisor.moveHomeStackTaskToTop(
-                                task.getTaskToReturnTo(), myReason)) {
-                            // Activity focus was already adjusted. Nothing else to do...
-                            return;
-                        }
+        if (!mStackSupervisor.isFocusedStack(this) || mService.mFocusedActivity != r) {
+            return;
+        }
+
+        final ActivityRecord next = topRunningActivityLocked();
+        final String myReason = reason + " adjustFocus";
+        if (next != r) {
+            if (next != null && StackId.keepFocusInStackIfPossible(mStackId)) {
+                // For freeform, docked, and pinned stacks we always keep the focus within the
+                // stack as long as there is a running activity in the stack that we can adjust
+                // focus to.
+                mService.setFocusedActivityLocked(next, myReason);
+                return;
+            } else {
+                final TaskRecord task = r.task;
+                if (r.frontOfTask && task == topTask() && task.isOverHomeStack()) {
+                    // For non-fullscreen stack, we want to move the focus to the next visible
+                    // stack to prevent the home screen from moving to the top and obscuring
+                    // other visible stacks.
+                    if (!mFullscreen
+                            && adjustFocusToNextVisibleStackLocked(null, myReason)) {
+                        return;
+                    }
+                    // Move the home stack to the top if this stack is fullscreen or there is no
+                    // other visible stack.
+                    if (mStackSupervisor.moveHomeStackTaskToTop(
+                            task.getTaskToReturnTo(), myReason)) {
+                        // Activity focus was already adjusted. Nothing else to do...
+                        return;
                     }
                 }
             }
+        }
 
-            final ActivityRecord top = mStackSupervisor.topRunningActivityLocked();
-            if (top != null) {
-                mService.setFocusedActivityLocked(top, myReason);
-            }
+        final ActivityRecord top = mStackSupervisor.topRunningActivityLocked();
+        if (top != null) {
+            mService.setFocusedActivityLocked(top, myReason);
         }
     }
 
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 2b3f04d..e9e02c1 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -76,6 +76,7 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
 import android.content.pm.ResolveInfo;
 import android.content.pm.UserInfo;
 import android.content.res.Configuration;
@@ -88,6 +89,7 @@
 import android.hardware.input.InputManagerInternal;
 import android.net.Uri;
 import android.os.Binder;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.Debug;
 import android.os.Handler;
@@ -184,7 +186,6 @@
     static final int CONTAINER_CALLBACK_TASK_LIST_EMPTY = FIRST_SUPERVISOR_STACK_MSG + 11;
     static final int LAUNCH_TASK_BEHIND_COMPLETE = FIRST_SUPERVISOR_STACK_MSG + 12;
     static final int SHOW_LOCK_TASK_ESCAPE_MESSAGE_MSG = FIRST_SUPERVISOR_STACK_MSG + 13;
-    static final int SHOW_NON_RESIZEABLE_DOCK_TOAST = FIRST_SUPERVISOR_STACK_MSG + 14;
 
     private static final String VIRTUAL_DISPLAY_BASE_NAME = "ActivityViewVirtualDisplay";
 
@@ -1708,6 +1709,46 @@
             return ActivityManager.START_SUCCESS;
         }
 
+        // If permissions need a review before any of the app components can run, we
+        // launch the review activity and pass a pending intent to start the activity
+        // we are to launching now after the review is completed.
+        if (Build.PERMISSIONS_REVIEW_REQUIRED && aInfo != null) {
+            if (mService.getPackageManagerInternalLocked().isPermissionsReviewRequired(
+                    aInfo.packageName, userId)) {
+                IIntentSender target = mService.getIntentSenderLocked(
+                        ActivityManager.INTENT_SENDER_ACTIVITY, callingPackage,
+                        callingUid, userId, null, null, 0, new Intent[]{intent},
+                        new String[]{resolvedType}, PendingIntent.FLAG_CANCEL_CURRENT
+                                | PendingIntent.FLAG_ONE_SHOT, null);
+
+                Intent newIntent = new Intent(Intent.ACTION_REVIEW_PERMISSIONS);
+                newIntent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+                newIntent.putExtra(Intent.EXTRA_PACKAGE_NAME, aInfo.packageName);
+                newIntent.putExtra(Intent.EXTRA_INTENT, new IntentSender(target));
+                if (resultRecord != null) {
+                    newIntent.putExtra(Intent.EXTRA_RESULT_NEEDED, true);
+                }
+                newIntent.setFlags(intent.getFlags());
+                intent = newIntent;
+
+                resolvedType = null;
+                callingUid = realCallingUid;
+                callingPid = realCallingPid;
+
+                aInfo = resolveActivity(intent, null, PackageManager.MATCH_DEFAULT_ONLY
+                        | ActivityManagerService.STOCK_PM_FLAGS, null, userId);
+
+                if (DEBUG_PERMISSIONS_REVIEW) {
+                    Slog.i(TAG, "START u" + userId + " {" + intent.toShortString(true, true,
+                            true, false) + "} from uid " + callingUid + " on display "
+                            + (container == null ? (mFocusedStack == null ?
+                            Display.DEFAULT_DISPLAY : mFocusedStack.mDisplayId) :
+                            (container.mActivityDisplay == null ? Display.DEFAULT_DISPLAY :
+                                    container.mActivityDisplay.mDisplayId)));
+                }
+            }
+        }
+
         ActivityRecord r = new ActivityRecord(mService, callerApp, callingUid, callingPackage,
                 intent, resolvedType, aInfo, mService.mConfiguration, resultRecord, resultWho,
                 requestCode, componentSpecified, voiceSession != null, this, container, options);
@@ -2592,6 +2633,11 @@
             mService.setFocusedActivityLocked(r, "startedActivity");
         }
         updateUserStackLocked(r.userId, targetStack);
+
+        if (!r.task.mResizeable && isStackDockedInEffect(targetStack.mStackId)) {
+            showNonResizeableDockToast(r.task.taskId);
+        }
+
         return ActivityManager.START_SUCCESS;
     }
 
@@ -3397,7 +3443,7 @@
         resumeTopActivitiesLocked();
 
         if (!task.mResizeable && isStackDockedInEffect(stackId)) {
-            showNonResizeableDockToast();
+            showNonResizeableDockToast(taskId);
         }
     }
 
@@ -4331,8 +4377,8 @@
         }
     }
 
-    void showNonResizeableDockToast() {
-        mHandler.sendEmptyMessage(SHOW_NON_RESIZEABLE_DOCK_TOAST);
+    private void showNonResizeableDockToast(int taskId) {
+        mWindowManager.scheduleShowNonResizeableDockToast(taskId);
     }
 
     void showLockTaskToast() {
@@ -4647,13 +4693,7 @@
                         }
                     }
                 } break;
-                case SHOW_NON_RESIZEABLE_DOCK_TOAST: {
-                    final Toast toast = Toast.makeText(
-                            mService.mContext,
-                            mService.mContext.getString(R.string.dock_non_resizeble_text),
-                            Toast.LENGTH_LONG);
-                    toast.show();
-                } break;
+
             }
         }
     }
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index c7228ce..f64b803 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -272,18 +272,18 @@
         }
     }
 
-    void noteProcessState(String name, int uid, int state) {
-        synchronized (mStats) {
-            mStats.noteProcessStateLocked(name, uid, state);
-        }
-    }
-
     void noteProcessFinish(String name, int uid) {
         synchronized (mStats) {
             mStats.noteProcessFinishLocked(name, uid);
         }
     }
 
+    void noteUidProcessState(int uid, int state) {
+        synchronized (mStats) {
+            mStats.noteUidProcessStateLocked(uid, state);
+        }
+    }
+
     // Public interface...
 
     public byte[] getStatistics() {
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index fb37eda..b160981 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -27,12 +27,16 @@
 import android.app.AppGlobals;
 import android.app.AppOpsManager;
 import android.app.BroadcastOptions;
+import android.app.PendingIntent;
 import android.content.ComponentName;
 import android.content.IIntentReceiver;
+import android.content.IIntentSender;
 import android.content.Intent;
+import android.content.IntentSender;
 import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
@@ -46,6 +50,7 @@
 import android.util.Slog;
 import android.util.TimeUtils;
 import com.android.server.DeviceIdleController;
+import com.android.server.LocalServices;
 
 import static com.android.server.am.ActivityManagerDebugConfig.*;
 
@@ -181,7 +186,7 @@
                 } break;
             }
         }
-    };
+    }
 
     private final class AppNotResponding implements Runnable {
         private final ProcessRecord mApp;
@@ -580,6 +585,17 @@
         }
 
         if (!skip) {
+            // If permissions need a review before any of the app components can run, we drop
+            // the broadcast and if the calling app is in the foreground and the broadcast is
+            // explicit we launch the review UI passing it a pending intent to send the skipped
+            // broadcast.
+            if (Build.PERMISSIONS_REVIEW_REQUIRED) {
+                if (!requestStartTargetPermissionsReviewIfNeededLocked(r, filter.packageName,
+                        filter.owningUserId)) {
+                    return;
+                }
+            }
+
             // If this is not being sent as an ordered broadcast, then we
             // don't want to touch the fields that keep track of the current
             // state of ordered broadcasts.
@@ -622,6 +638,54 @@
         }
     }
 
+    private boolean requestStartTargetPermissionsReviewIfNeededLocked(
+            BroadcastRecord receiverRecord, String receivingPackageName,
+            final int receivingUserId) {
+        if (!mService.getPackageManagerInternalLocked().isPermissionsReviewRequired(
+                receivingPackageName, receivingUserId)) {
+            return true;
+        }
+
+        final boolean callerForeground = receiverRecord.callerApp != null
+                ? receiverRecord.callerApp.setSchedGroup != Process.THREAD_GROUP_BG_NONINTERACTIVE
+                : true;
+
+        // Show a permission review UI only for explicit broadcast from a foreground app
+        if (callerForeground && receiverRecord.intent.getComponent() != null) {
+            IIntentSender target = mService.getIntentSenderLocked(
+                    ActivityManager.INTENT_SENDER_BROADCAST, receiverRecord.callerPackage,
+                    receiverRecord.callingUid, receiverRecord.userId, null, null, 0,
+                    new Intent[]{receiverRecord.intent},
+                    new String[]{receiverRecord.intent.resolveType(mService.mContext
+                            .getContentResolver())},
+                    PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT
+                            | PendingIntent.FLAG_IMMUTABLE, null);
+
+            final Intent intent = new Intent(Intent.ACTION_REVIEW_PERMISSIONS);
+            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+                    | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+            intent.putExtra(Intent.EXTRA_PACKAGE_NAME, receivingPackageName);
+            intent.putExtra(Intent.EXTRA_INTENT, new IntentSender(target));
+
+            if (DEBUG_PERMISSIONS_REVIEW) {
+                Slog.i(TAG, "u" + receivingUserId + " Launching permission review for package "
+                        + receivingPackageName);
+            }
+
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mService.mContext.startActivityAsUser(intent, new UserHandle(receivingUserId));
+                }
+            });
+        } else {
+            Slog.w(TAG, "u" + receivingUserId + " Receiving a broadcast in package"
+                    + receivingPackageName + " requires a permissions review");
+        }
+
+        return false;
+    }
+
     final void scheduleTempWhitelistLocked(int uid, long duration, BroadcastRecord r) {
         if (duration > Integer.MAX_VALUE) {
             duration = Integer.MAX_VALUE;
@@ -1007,6 +1071,18 @@
                 }
             }
 
+            // If permissions need a review before any of the app components can run, we drop
+            // the broadcast and if the calling app is in the foreground and the broadcast is
+            // explicit we launch the review UI passing it a pending intent to send the skipped
+            // broadcast.
+            if (Build.PERMISSIONS_REVIEW_REQUIRED && !skip) {
+                if (!requestStartTargetPermissionsReviewIfNeededLocked(r,
+                        info.activityInfo.packageName, UserHandle.getUserId(
+                                info.activityInfo.applicationInfo.uid))) {
+                    skip = true;
+                }
+            }
+
             if (skip) {
                 if (DEBUG_BROADCAST)  Slog.v(TAG_BROADCAST,
                         "Skipping delivery of ordered [" + mQueueName + "] "
diff --git a/services/core/java/com/android/server/am/EventLogTags.logtags b/services/core/java/com/android/server/am/EventLogTags.logtags
index 78b5f33..0397553 100644
--- a/services/core/java/com/android/server/am/EventLogTags.logtags
+++ b/services/core/java/com/android/server/am/EventLogTags.logtags
@@ -91,7 +91,7 @@
 30042 am_activity_fully_drawn_time (User|1|5),(Token|1|5),(Component Name|3),(time|2|3)
 
 # Activity focused
-30043 am_focused_activity (User|1|5),(Component Name|3)
+30043 am_focused_activity (User|1|5),(Component Name|3),(Reason|3)
 
 # Stack focus
 30044 am_focused_stack (User|1|5),(Focused Stack Id|1|5),(Last Focused Stack Id|1|5),(Reason|3)
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 9c29149..3e0ae17 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -18,6 +18,8 @@
 
 import static android.Manifest.permission.INTERACT_ACROSS_USERS;
 import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+import static android.app.ActivityManager.USER_OP_ERROR_IS_SYSTEM;
+import static android.app.ActivityManager.USER_OP_ERROR_RELATED_USERS_CANNOT_STOP;
 import static android.app.ActivityManager.USER_OP_IS_CURRENT;
 import static android.app.ActivityManager.USER_OP_SUCCESS;
 import static android.os.Process.SYSTEM_UID;
@@ -34,6 +36,7 @@
 import static com.android.server.am.ActivityManagerService.SYSTEM_USER_START_MSG;
 import static com.android.server.am.ActivityManagerService.USER_SWITCH_TIMEOUT_MSG;
 
+import android.annotation.NonNull;
 import android.app.ActivityManager;
 import android.app.AppOpsManager;
 import android.app.Dialog;
@@ -56,11 +59,11 @@
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.os.ServiceManager;
-import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.os.storage.IMountService;
 import android.os.storage.StorageManager;
+import android.util.IntArray;
 import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -78,6 +81,8 @@
 import java.util.List;
 import java.util.Set;
 
+import libcore.util.EmptyArray;
+
 /**
  * Helper class for {@link ActivityManagerService} responsible for multi-user functionality.
  */
@@ -152,39 +157,44 @@
             finishUserBoot(uss);
 
             startProfilesLocked();
+            stopRunningUsersLocked(MAX_RUNNING_USERS);
+        }
+    }
 
-            int num = mUserLru.size();
-            int i = 0;
-            while (num > MAX_RUNNING_USERS && i < mUserLru.size()) {
-                Integer oldUserId = mUserLru.get(i);
-                UserState oldUss = mStartedUsers.get(oldUserId);
-                if (oldUss == null) {
-                    // Shouldn't happen, but be sane if it does.
-                    mUserLru.remove(i);
-                    num--;
-                    continue;
-                }
-                if (oldUss.mState == UserState.STATE_STOPPING
-                        || oldUss.mState == UserState.STATE_SHUTDOWN) {
-                    // This user is already stopping, doesn't count.
-                    num--;
-                    i++;
-                    continue;
-                }
-                if (oldUserId == UserHandle.USER_SYSTEM || oldUserId == mCurrentUserId) {
-                    // Owner/System user and current user can't be stopped. We count it as running
-                    // when it is not a pure system user.
-                    if (UserInfo.isSystemOnly(oldUserId)) {
-                        num--;
-                    }
-                    i++;
-                    continue;
-                }
-                // This is a user to be stopped.
-                stopUserLocked(oldUserId, null);
+    void stopRunningUsersLocked(int maxRunningUsers) {
+        int num = mUserLru.size();
+        int i = 0;
+        while (num > maxRunningUsers && i < mUserLru.size()) {
+            Integer oldUserId = mUserLru.get(i);
+            UserState oldUss = mStartedUsers.get(oldUserId);
+            if (oldUss == null) {
+                // Shouldn't happen, but be sane if it does.
+                mUserLru.remove(i);
+                num--;
+                continue;
+            }
+            if (oldUss.mState == UserState.STATE_STOPPING
+                    || oldUss.mState == UserState.STATE_SHUTDOWN) {
+                // This user is already stopping, doesn't count.
                 num--;
                 i++;
+                continue;
             }
+            if (oldUserId == UserHandle.USER_SYSTEM || oldUserId == mCurrentUserId) {
+                // Owner/System user and current user can't be stopped. We count it as running
+                // when it is not a pure system user.
+                if (UserInfo.isSystemOnly(oldUserId)) {
+                    num--;
+                }
+                i++;
+                continue;
+            }
+            // This is a user to be stopped.
+            if (stopUsersLocked(oldUserId, false, null) != USER_OP_SUCCESS) {
+                num--;
+            }
+            num--;
+            i++;
         }
     }
 
@@ -205,7 +215,7 @@
         }
     }
 
-    int stopUser(final int userId, final IStopUserCallback callback) {
+    int stopUser(final int userId, final boolean force, final IStopUserCallback callback) {
         if (mService.checkCallingPermission(INTERACT_ACROSS_USERS_FULL)
                 != PackageManager.PERMISSION_GRANTED) {
             String msg = "Permission Denial: switchUser() from pid="
@@ -221,16 +231,44 @@
         mService.enforceShellRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES,
                 userId);
         synchronized (mService) {
-            return stopUserLocked(userId, callback);
+            return stopUsersLocked(userId, force, callback);
         }
     }
 
-    private int stopUserLocked(final int userId, final IStopUserCallback callback) {
-        if (DEBUG_MU) Slog.i(TAG, "stopUserLocked userId=" + userId);
-        if (mCurrentUserId == userId && mTargetUserId == UserHandle.USER_NULL) {
+    /**
+     * Stops the user along with its related users. The method calls
+     * {@link #getUsersToStopLocked(int)} to determine the list of users that should be stopped.
+     */
+    private int stopUsersLocked(final int userId, boolean force, final IStopUserCallback callback) {
+        if (userId == UserHandle.USER_SYSTEM) {
+            return USER_OP_ERROR_IS_SYSTEM;
+        }
+        if (isCurrentUserLocked(userId)) {
             return USER_OP_IS_CURRENT;
         }
+        int[] usersToStop = getUsersToStopLocked(userId);
+        // If one of related users is system or current, no related users should be stopped
+        for (int i = 0; i < usersToStop.length; i++) {
+            int relatedUserId = usersToStop[i];
+            if ((UserHandle.USER_SYSTEM == relatedUserId) || isCurrentUserLocked(relatedUserId)) {
+                if (DEBUG_MU) Slog.i(TAG, "stopUsersLocked cannot stop related user "
+                        + relatedUserId);
+                // We still need to stop the requested user if it's a force stop.
+                if (force) {
+                    stopSingleUserLocked(userId, callback);
+                }
+                return USER_OP_ERROR_RELATED_USERS_CANNOT_STOP;
+            }
+        }
+        if (DEBUG_MU) Slog.i(TAG, "stopUsersLocked usersToStop=" + Arrays.toString(usersToStop));
+        for (int userIdToStop : usersToStop) {
+            stopSingleUserLocked(userIdToStop, userIdToStop == userId ? callback : null);
+        }
+        return USER_OP_SUCCESS;
+    }
 
+    private void stopSingleUserLocked(final int userId, final IStopUserCallback callback) {
+        if (DEBUG_MU) Slog.i(TAG, "stopSingleUserLocked userId=" + userId);
         final UserState uss = mStartedUsers.get(userId);
         if (uss == null) {
             // User is not started, nothing to do...  but we do need to
@@ -246,7 +284,7 @@
                     }
                 });
             }
-            return USER_OP_SUCCESS;
+            return;
         }
 
         if (callback != null) {
@@ -307,8 +345,6 @@
                 Binder.restoreCallingIdentity(ident);
             }
         }
-
-        return USER_OP_SUCCESS;
     }
 
     void finishUserStop(UserState uss) {
@@ -350,6 +386,36 @@
         }
     }
 
+    /**
+     * Determines the list of users that should be stopped together with the specified
+     * {@code userId}. The returned list includes {@code userId}.
+     */
+    private @NonNull int[] getUsersToStopLocked(int userId) {
+        int startedUsersSize = mStartedUsers.size();
+        IntArray userIds = new IntArray();
+        userIds.add(userId);
+        synchronized (mUserProfileGroupIdsSelfLocked) {
+            int userGroupId = mUserProfileGroupIdsSelfLocked.get(userId,
+                    UserInfo.NO_PROFILE_GROUP_ID);
+            for (int i = 0; i < startedUsersSize; i++) {
+                UserState uss = mStartedUsers.valueAt(i);
+                int startedUserId = uss.mHandle.getIdentifier();
+                // Skip unrelated users (profileGroupId mismatch)
+                int startedUserGroupId = mUserProfileGroupIdsSelfLocked.get(startedUserId,
+                        UserInfo.NO_PROFILE_GROUP_ID);
+                boolean sameGroup = (userGroupId != UserInfo.NO_PROFILE_GROUP_ID)
+                        && (userGroupId == startedUserGroupId);
+                // userId has already been added
+                boolean sameUserId = startedUserId == userId;
+                if (!sameGroup || sameUserId) {
+                    continue;
+                }
+                userIds.add(startedUserId);
+            }
+        }
+        return userIds.toArray();
+    }
+
     private void forceStopUserLocked(int userId, String reason) {
         mService.forceStopPackageLocked(null, -1, false, false, true, false, false,
                 userId, reason);
@@ -362,7 +428,6 @@
                 null, false, false, MY_PID, SYSTEM_UID, UserHandle.USER_ALL);
     }
 
-
     /**
      * Stops the guest user if it has gone to the background.
      */
@@ -380,7 +445,7 @@
                 UserInfo userInfo = getUserInfo(oldUserId);
                 if (userInfo.isGuest()) {
                     // This is a user to be stopped.
-                    stopUserLocked(oldUserId, null);
+                    stopUsersLocked(oldUserId, true, null);
                     break;
                 }
             }
@@ -476,7 +541,7 @@
                 // If the user we are switching to is not currently started, then
                 // we need to start it now.
                 if (mStartedUsers.get(userId) == null) {
-                    mStartedUsers.put(userId, new UserState(new UserHandle(userId)));
+                    mStartedUsers.put(userId, new UserState(UserHandle.of(userId)));
                     updateStartedUserArrayLocked();
                     needStart = true;
                 }
@@ -695,6 +760,24 @@
         mUserSwitchObservers.finishBroadcast();
     }
 
+    private void stopBackgroundUsersIfEnforced(int oldUserId) {
+        // Never stop system user
+        if (oldUserId == UserHandle.USER_SYSTEM) {
+            return;
+        }
+        // For now, only check for user restriction. Additional checks can be added here
+        boolean disallowRunInBg = hasUserRestriction(UserManager.DISALLOW_RUN_IN_BACKGROUND,
+                oldUserId);
+        if (!disallowRunInBg) {
+            return;
+        }
+        synchronized (mService) {
+            if (DEBUG_MU) Slog.i(TAG, "stopBackgroundUsersIfEnforced stopping " + oldUserId
+                    + " and related users");
+            stopUsersLocked(oldUserId, false, null);
+        }
+    }
+
     void timeoutUserSwitch(UserState uss, int oldUserId, int newUserId) {
         synchronized (mService) {
             Slog.wtf(TAG, "User switch timeout: from " + oldUserId + " to " + newUserId);
@@ -747,7 +830,7 @@
     }
 
     void continueUserSwitch(UserState uss, int oldUserId, int newUserId) {
-        completeSwitchAndInitialize(uss, newUserId, false, true);
+        completeSwitchAndInitialize(uss, oldUserId, newUserId, false, true);
     }
 
     void onUserInitialized(UserState uss, boolean foreground, int oldUserId, int newUserId) {
@@ -756,10 +839,10 @@
                 moveUserToForegroundLocked(uss, oldUserId, newUserId);
             }
         }
-        completeSwitchAndInitialize(uss, newUserId, true, false);
+        completeSwitchAndInitialize(uss, oldUserId, newUserId, true, false);
     }
 
-    void completeSwitchAndInitialize(UserState uss, int newUserId,
+    void completeSwitchAndInitialize(UserState uss, int oldUserId, int newUserId,
             boolean clearInitializing, boolean clearSwitching) {
         boolean unfrozen = false;
         synchronized (mService) {
@@ -781,6 +864,7 @@
                     newUserId, 0));
         }
         stopGuestUserIfBackground();
+        stopBackgroundUsersIfEnforced(oldUserId);
     }
 
     void moveUserToForegroundLocked(UserState uss, int oldUserId, int newUserId) {
@@ -1033,12 +1117,11 @@
         if ((flags & ActivityManager.FLAG_OR_STOPPED) != 0) {
             return true;
         }
-        if ((flags & ActivityManager.FLAG_AND_LOCKED) != 0) {
-            // If user is currently locked, we fall through to default "running"
-            // behavior below
-            if (state.unlocked) {
-                return false;
-            }
+        if ((flags & ActivityManager.FLAG_AND_LOCKED) != 0 && state.unlocked) {
+            return false;
+        }
+        if ((flags & ActivityManager.FLAG_AND_UNLOCKED) != 0 && !state.unlocked) {
+            return false;
         }
         return state.mState != UserState.STATE_STOPPING
                 && state.mState != UserState.STATE_SHUTDOWN;
@@ -1074,6 +1157,10 @@
         return mCurrentUserId;
     }
 
+    private boolean isCurrentUserLocked(int userId) {
+        return mCurrentUserId == userId || mTargetUserId == userId;
+    }
+
     int setTargetUserIdLocked(int targetUserId) {
         return mTargetUserId = targetUserId;
     }
diff --git a/services/core/java/com/android/server/audio/MediaFocusControl.java b/services/core/java/com/android/server/audio/MediaFocusControl.java
index d44d89d..278d70b 100644
--- a/services/core/java/com/android/server/audio/MediaFocusControl.java
+++ b/services/core/java/com/android/server/audio/MediaFocusControl.java
@@ -387,8 +387,11 @@
     /** @see AudioManager#requestAudioFocus(AudioManager.OnAudioFocusChangeListener, int, int, int) */
     protected int requestAudioFocus(AudioAttributes aa, int focusChangeHint, IBinder cb,
             IAudioFocusDispatcher fd, String clientId, String callingPackageName, int flags) {
-        Log.i(TAG, " AudioFocus  requestAudioFocus() from " + clientId + " req=" + focusChangeHint +
-                "flags=0x" + Integer.toHexString(flags));
+        Log.i(TAG, " AudioFocus  requestAudioFocus() from uid/pid " + Binder.getCallingUid()
+                + "/" + Binder.getCallingPid()
+                + " clientId=" + clientId
+                + " req=" + focusChangeHint
+                + " flags=0x" + Integer.toHexString(flags));
         // we need a valid binder callback for clients
         if (!cb.pingBinder()) {
             Log.e(TAG, " AudioFocus DOA client for requestAudioFocus(), aborting.");
@@ -481,7 +484,9 @@
      * */
     protected int abandonAudioFocus(IAudioFocusDispatcher fl, String clientId, AudioAttributes aa) {
         // AudioAttributes are currently ignored, to be used for zones
-        Log.i(TAG, " AudioFocus  abandonAudioFocus() from " + clientId);
+        Log.i(TAG, " AudioFocus  abandonAudioFocus() from uid/pid " + Binder.getCallingUid()
+                + "/" + Binder.getCallingPid()
+                + " clientId=" + clientId);
         try {
             // this will take care of notifying the new focus owner if needed
             synchronized(mAudioFocusLock) {
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 81ae8ac..ae8fca8 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -16,6 +16,7 @@
 
 package com.android.server.input;
 
+import android.annotation.Nullable;
 import android.view.Display;
 import com.android.internal.os.SomeArgs;
 import com.android.internal.R;
@@ -83,6 +84,9 @@
 import android.view.Surface;
 import android.view.ViewConfiguration;
 import android.view.WindowManagerPolicy;
+import android.view.inputmethod.InputMethod;
+import android.view.inputmethod.InputMethodInfo;
+import android.view.inputmethod.InputMethodSubtype;
 import android.widget.Toast;
 
 import java.io.File;
@@ -116,6 +120,7 @@
     private static final int MSG_UPDATE_KEYBOARD_LAYOUTS = 4;
     private static final int MSG_RELOAD_DEVICE_ALIASES = 5;
     private static final int MSG_DELIVER_TABLET_MODE_CHANGED = 6;
+    private static final int MSG_INPUT_METHOD_SUBTYPE_CHANGED = 7;
 
     // Pointer to native input manager service object.
     private final long mPtr;
@@ -1206,6 +1211,15 @@
         }
     }
 
+    // Must be called on handler.
+    private void handleSwitchInputMethodSubtype(int userId,
+            @Nullable InputMethodInfo inputMethodInfo, @Nullable InputMethodSubtype subtype) {
+        if (DEBUG) {
+            Slog.i(TAG, "InputMethodSubtype changed: userId=" + userId
+                    + " ime=" + inputMethodInfo + " subtype=" + subtype);
+        }
+    }
+
     public void switchKeyboardLayout(int deviceId, int direction) {
         mHandler.obtainMessage(MSG_SWITCH_KEYBOARD_LAYOUT, deviceId, direction).sendToTarget();
     }
@@ -1757,12 +1771,22 @@
                 case MSG_RELOAD_DEVICE_ALIASES:
                     reloadDeviceAliases();
                     break;
-                case MSG_DELIVER_TABLET_MODE_CHANGED:
+                case MSG_DELIVER_TABLET_MODE_CHANGED: {
                     SomeArgs args = (SomeArgs) msg.obj;
                     long whenNanos = (args.argi1 & 0xFFFFFFFFl) | ((long) args.argi2 << 32);
                     boolean inTabletMode = (boolean) args.arg1;
                     deliverTabletModeChanged(whenNanos, inTabletMode);
                     break;
+                }
+                case MSG_INPUT_METHOD_SUBTYPE_CHANGED: {
+                    final int userId = msg.arg1;
+                    final SomeArgs args = (SomeArgs) msg.obj;
+                    final InputMethodInfo inputMethodInfo = (InputMethodInfo) args.arg1;
+                    final InputMethodSubtype subtype = (InputMethodSubtype) args.arg2;
+                    args.recycle();
+                    handleSwitchInputMethodSubtype(userId, inputMethodInfo, subtype);
+                    break;
+                }
             }
         }
     }
@@ -1920,5 +1944,15 @@
         public void setInteractive(boolean interactive) {
             nativeSetInteractive(mPtr, interactive);
         }
+
+        @Override
+        public void onInputMethodSubtypeChanged(int userId,
+                @Nullable InputMethodInfo inputMethodInfo, @Nullable InputMethodSubtype subtype) {
+            final SomeArgs someArgs = SomeArgs.obtain();
+            someArgs.arg1 = inputMethodInfo;
+            someArgs.arg2 = subtype;
+            mHandler.obtainMessage(MSG_INPUT_METHOD_SUBTYPE_CHANGED, userId, 0, someArgs)
+                    .sendToTarget();
+        }
     }
 }
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 38893b8..e0b7370 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -1634,6 +1634,14 @@
         }
 
         @Override
+        public boolean removeAutomaticZenRules(String packageName) throws RemoteException {
+            Preconditions.checkNotNull(packageName, "Package name is null");
+            enforceSystemOrSystemUI("removeAutomaticZenRules");
+
+            return mZenModeHelper.removeAutomaticZenRules(packageName, "removeAutomaticZenRules");
+        }
+
+        @Override
         public void setInterruptionFilter(String pkg, int filter) throws RemoteException {
             enforcePolicyAccess(pkg, "setInterruptionFilter");
             final int zen = NotificationManager.zenModeFromInterruptionFilter(filter, -1);
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index dbdc3f4..3c891df 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -41,6 +41,7 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
+import android.os.Process;
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.provider.Settings.Global;
@@ -274,7 +275,7 @@
             newConfig = mConfig.copy();
         }
         final String ruleId = automaticZenRule.getId();
-        ZenModeConfig.ZenRule rule = new ZenModeConfig.ZenRule();
+        ZenModeConfig.ZenRule rule;
         if (ruleId == null) {
             throw new IllegalArgumentException("Rule doesn't exist");
         } else {
@@ -307,13 +308,32 @@
         return setConfig(newConfig, reason, true);
     }
 
+    public boolean removeAutomaticZenRules(String packageName, String reason) {
+        ZenModeConfig newConfig;
+        synchronized (mConfig) {
+            if (mConfig == null) return false;
+            newConfig = mConfig.copy();
+        }
+        for (int i = newConfig.automaticRules.size() - 1; i >= 0; i--) {
+            ZenRule rule = newConfig.automaticRules.get(newConfig.automaticRules.keyAt(i));
+            if (rule.component.getPackageName().equals(packageName)
+                    && canManageAutomaticZenRule(rule)) {
+                newConfig.automaticRules.removeAt(i);
+            }
+        }
+        return setConfig(newConfig, reason, true);
+    }
+
     public boolean canManageAutomaticZenRule(ZenRule rule) {
-        if (mContext.checkCallingPermission(android.Manifest.permission.MANAGE_NOTIFICATIONS)
+        final int callingUid = Binder.getCallingUid();
+        if (callingUid == 0 || callingUid == Process.SYSTEM_UID) {
+            return true;
+        } else if (mContext.checkCallingPermission(android.Manifest.permission.MANAGE_NOTIFICATIONS)
                 == PackageManager.PERMISSION_GRANTED) {
             return true;
         } else {
-            String[] packages = mContext.getPackageManager().getPackagesForUid(
-                    Binder.getCallingUid());
+            String[] packages =
+                    mContext.getPackageManager().getPackagesForUid(Binder.getCallingUid());
             if (packages != null) {
                 final int packageCount = packages.length;
                 for (int i = 0; i < packageCount; i++) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index c7d1171..0fde27f 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -26,6 +26,7 @@
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED;
+import static android.content.pm.PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_SYSTEM_FIXED;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED;
@@ -400,14 +401,11 @@
     /** Permission grant: grant the permission as an install permission. */
     private static final int GRANT_INSTALL = 2;
 
-    /** Permission grant: grant the permission as an install permission for a legacy app. */
-    private static final int GRANT_INSTALL_LEGACY = 3;
-
     /** Permission grant: grant the permission as a runtime one. */
-    private static final int GRANT_RUNTIME = 4;
+    private static final int GRANT_RUNTIME = 3;
 
     /** Permission grant: grant as runtime a permission that was granted as an install time one. */
-    private static final int GRANT_UPGRADE = 5;
+    private static final int GRANT_UPGRADE = 4;
 
     /** Canonical intent used to identify what counts as a "web browser" app */
     private static final Intent sBrowserIntent;
@@ -3688,6 +3686,16 @@
 
             enforceDeclaredAsUsedAndRuntimeOrDevelopmentPermission(pkg, bp);
 
+            // If a permission review is required for legacy apps we represent
+            // their permissions as always granted runtime ones since we need
+            // to keep the review required permission flag per user while an
+            // install permission's state is shared across all users.
+            if (Build.PERMISSIONS_REVIEW_REQUIRED
+                    && pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M
+                    && bp.isRuntime()) {
+                return;
+            }
+
             uid = UserHandle.getUid(userId, pkg.applicationInfo.uid);
             sb = (SettingBase) pkg.mExtras;
             if (sb == null) {
@@ -3788,6 +3796,16 @@
 
             enforceDeclaredAsUsedAndRuntimeOrDevelopmentPermission(pkg, bp);
 
+            // If a permission review is required for legacy apps we represent
+            // their permissions as always granted runtime ones since we need
+            // to keep the review required permission flag per user while an
+            // install permission's state is shared across all users.
+            if (Build.PERMISSIONS_REVIEW_REQUIRED
+                    && pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M
+                    && bp.isRuntime()) {
+                return;
+            }
+
             SettingBase sb = (SettingBase) pkg.mExtras;
             if (sb == null) {
                 throw new IllegalArgumentException("Unknown package: " + packageName);
@@ -3906,6 +3924,7 @@
             flagValues &= ~PackageManager.FLAG_PERMISSION_SYSTEM_FIXED;
             flagMask &= ~PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT;
             flagValues &= ~PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT;
+            flagValues &= ~PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
         }
 
         synchronized (mPackages) {
@@ -8641,6 +8660,8 @@
             }
 
             final int level = bp.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE;
+            final boolean appSupportsRuntimePermissions = pkg.applicationInfo.targetSdkVersion
+                    >= Build.VERSION_CODES.M;
             switch (level) {
                 case PermissionInfo.PROTECTION_NORMAL: {
                     // For all apps normal permissions are install time ones.
@@ -8648,9 +8669,13 @@
                 } break;
 
                 case PermissionInfo.PROTECTION_DANGEROUS: {
-                    if (pkg.applicationInfo.targetSdkVersion <= Build.VERSION_CODES.LOLLIPOP_MR1) {
+                    // If a permission review is required for legacy apps we represent
+                    // their permissions as always granted runtime ones since we need
+                    // to keep the review required permission flag per user while an
+                    // install permission's state is shared across all users.
+                    if (!appSupportsRuntimePermissions && !Build.PERMISSIONS_REVIEW_REQUIRED) {
                         // For legacy apps dangerous permissions are install time ones.
-                        grant = GRANT_INSTALL_LEGACY;
+                        grant = GRANT_INSTALL;
                     } else if (origPermissions.hasInstallPermission(bp.name)) {
                         // For legacy apps that became modern, install becomes runtime.
                         grant = GRANT_UPGRADE;
@@ -8697,7 +8722,7 @@
                 switch (grant) {
                     case GRANT_INSTALL: {
                         // Revoke this as runtime permission to handle the case of
-                        // a runtime permission being downgraded to an install one.
+                        // a runtime permission being downgraded to an install one. Also in permission review mode we keep dangerous permissions for legacy apps
                         for (int userId : UserManagerService.getInstance().getUserIds()) {
                             if (origPermissions.getRuntimePermissionState(
                                     bp.name, userId) != null) {
@@ -8717,20 +8742,12 @@
                         }
                     } break;
 
-                    case GRANT_INSTALL_LEGACY: {
-                        // Grant an install permission.
-                        if (permissionsState.grantInstallPermission(bp) !=
-                                PermissionsState.PERMISSION_OPERATION_FAILURE) {
-                            changedInstallPermission = true;
-                        }
-                    } break;
-
                     case GRANT_RUNTIME: {
                         // Grant previously granted runtime permissions.
                         for (int userId : UserManagerService.getInstance().getUserIds()) {
                             PermissionState permissionState = origPermissions
                                     .getRuntimePermissionState(bp.name, userId);
-                            final int flags = permissionState != null
+                            int flags = permissionState != null
                                     ? permissionState.getFlags() : 0;
                             if (origPermissions.hasRuntimePermission(bp.name, userId)) {
                                 if (permissionsState.grantRuntimePermission(bp, userId) ==
@@ -8739,6 +8756,27 @@
                                     changedRuntimePermissionUserIds = ArrayUtils.appendInt(
                                             changedRuntimePermissionUserIds, userId);
                                 }
+                                // If the app supports runtime permissions no need for a review.
+                                if (Build.PERMISSIONS_REVIEW_REQUIRED
+                                        && appSupportsRuntimePermissions
+                                        && (flags & PackageManager
+                                                .FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
+                                    flags &= ~PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
+                                    // Since we changed the flags, we have to write.
+                                    changedRuntimePermissionUserIds = ArrayUtils.appendInt(
+                                            changedRuntimePermissionUserIds, userId);
+                                }
+                            } else if (Build.PERMISSIONS_REVIEW_REQUIRED
+                                    && !appSupportsRuntimePermissions) {
+                                // For legacy apps that need a permission review, every new
+                                // runtime permission is granted but it is pending a review.
+                                if ((flags & FLAG_PERMISSION_REVIEW_REQUIRED) == 0) {
+                                    permissionsState.grantRuntimePermission(bp, userId);
+                                    flags |= FLAG_PERMISSION_REVIEW_REQUIRED;
+                                    // We changed the permission and flags, hence have to write.
+                                    changedRuntimePermissionUserIds = ArrayUtils.appendInt(
+                                            changedRuntimePermissionUserIds, userId);
+                                }
                             }
                             // Propagate the permission flags.
                             permissionsState.updatePermissionFlags(bp, userId, flags, flags);
@@ -11590,7 +11628,7 @@
         }
 
         int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException {
-            if (origin.staged) {
+            if (origin.staged && origin.cid != null) {
                 if (DEBUG_INSTALL) Slog.d(TAG, origin.cid + " already staged; skipping copy");
                 cid = origin.cid;
                 setMountPath(PackageHelper.getSdDir(cid));
@@ -13800,9 +13838,11 @@
             return;
         }
 
-        final int userSettableFlags = FLAG_PERMISSION_USER_SET
+        // These are flags that can change base on user actions.
+        final int userSettableMask = FLAG_PERMISSION_USER_SET
                 | FLAG_PERMISSION_USER_FIXED
-                | FLAG_PERMISSION_REVOKE_ON_UPGRADE;
+                | FLAG_PERMISSION_REVOKE_ON_UPGRADE
+                | FLAG_PERMISSION_REVIEW_REQUIRED;
 
         final int policyOrSystemFlags = FLAG_PERMISSION_SYSTEM_FIXED
                 | FLAG_PERMISSION_POLICY_FIXED;
@@ -13843,7 +13883,14 @@
             // Always clear the user settable flags.
             final boolean hasInstallState = permissionsState.getInstallPermissionState(
                     bp.name) != null;
-            if (permissionsState.updatePermissionFlags(bp, userId, userSettableFlags, 0)) {
+            // If permission review is enabled and this is a legacy app, mark the
+            // permission as requiring a review as this is the initial state.
+            int flags = 0;
+            if (Build.PERMISSIONS_REVIEW_REQUIRED
+                    && ps.pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M) {
+                flags |= FLAG_PERMISSION_REVIEW_REQUIRED;
+            }
+            if (permissionsState.updatePermissionFlags(bp, userId, userSettableMask, flags)) {
                 if (hasInstallState) {
                     writeInstallPermissions = true;
                 } else {
@@ -13867,7 +13914,9 @@
                         != PERMISSION_OPERATION_FAILURE) {
                     writeRuntimePermissions = true;
                 }
-            } else {
+            // If permission review is enabled the permissions for a legacy apps
+            // are represented as constantly granted runtime ones, so don't revoke.
+            } else if ((flags & FLAG_PERMISSION_REVIEW_REQUIRED) == 0) {
                 // Otherwise, reset the permission.
                 final int revokeResult = permissionsState.revokeRuntimePermission(bp, userId);
                 switch (revokeResult) {
@@ -16760,6 +16809,15 @@
 
     void newUserCreated(final int userHandle) {
         mDefaultPermissionPolicy.grantDefaultPermissions(userHandle);
+        // If permission review for legacy apps is required, we represent
+        // dagerous permissions for such apps as always granted runtime
+        // permissions to keep per user flag state whether review is needed.
+        // Hence, if a new user is added we have to propagate dangerous
+        // permission grants for these legacy apps.
+        if (Build.PERMISSIONS_REVIEW_REQUIRED) {
+            updatePermissionsLPw(null, null, UPDATE_PERMISSIONS_ALL
+                    | UPDATE_PERMISSIONS_REPLACE_ALL);
+        }
     }
 
     @Override
@@ -17175,6 +17233,7 @@
                         packageName, userId);
             }
         }
+
         @Override
         public void grantDefaultPermissionsToDefaultSimCallManager(String packageName, int userId) {
             synchronized (mPackages) {
@@ -17210,6 +17269,30 @@
                 }
             }
         }
+
+        @Override
+        public boolean isPermissionsReviewRequired(String packageName, int userId) {
+            synchronized (mPackages) {
+                // If we do not support permission review, done.
+                if (!Build.PERMISSIONS_REVIEW_REQUIRED) {
+                    return false;
+                }
+
+                PackageSetting packageSetting = mSettings.mPackages.get(packageName);
+                if (packageSetting == null) {
+                    return false;
+                }
+
+                // Permission review applies only to apps not supporting the new permission model.
+                if (packageSetting.pkg.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.M) {
+                    return false;
+                }
+
+                // Legacy apps have the permission and get user consent on launch.
+                PermissionsState permissionsState = packageSetting.getPermissionsState();
+                return permissionsState.isPermissionReviewRequired(userId);
+            }
+        }
     }
 
     @Override
diff --git a/services/core/java/com/android/server/pm/PermissionsState.java b/services/core/java/com/android/server/pm/PermissionsState.java
index 57ef284..007b738 100644
--- a/services/core/java/com/android/server/pm/PermissionsState.java
+++ b/services/core/java/com/android/server/pm/PermissionsState.java
@@ -16,11 +16,13 @@
 
 package com.android.server.pm;
 
+import android.content.pm.PackageManager;
 import android.os.UserHandle;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 
 import android.util.SparseArray;
+import android.util.SparseBooleanArray;
 import com.android.internal.util.ArrayUtils;
 
 import java.util.ArrayList;
@@ -64,6 +66,8 @@
 
     private int[] mGlobalGids = NO_GIDS;
 
+    private SparseBooleanArray mPermissionReviewRequired;
+
     public PermissionsState() {
         /* do nothing */
     }
@@ -116,6 +120,28 @@
             mGlobalGids = Arrays.copyOf(other.mGlobalGids,
                     other.mGlobalGids.length);
         }
+
+        if (mPermissionReviewRequired != null) {
+            if (other.mPermissionReviewRequired == null) {
+                mPermissionReviewRequired = null;
+            } else {
+                mPermissionReviewRequired.clear();
+            }
+        }
+        if (other.mPermissionReviewRequired != null) {
+            if (mPermissionReviewRequired == null) {
+                mPermissionReviewRequired = new SparseBooleanArray();
+            }
+            final int userCount = other.mPermissionReviewRequired.size();
+            for (int i = 0; i < userCount; i++) {
+                final boolean reviewRequired = other.mPermissionReviewRequired.valueAt(i);
+                mPermissionReviewRequired.put(i, reviewRequired);
+            }
+        }
+    }
+
+    public boolean isPermissionReviewRequired(int userId) {
+        return mPermissionReviewRequired != null && mPermissionReviewRequired.get(userId);
     }
 
     /**
@@ -357,7 +383,28 @@
             permissionData = ensurePermissionData(permission);
         }
 
-        return permissionData.updateFlags(userId, flagMask, flagValues);
+        final int oldFlags = permissionData.getFlags(userId);
+
+        final boolean updated = permissionData.updateFlags(userId, flagMask, flagValues);
+        if (updated) {
+            final int newFlags = permissionData.getFlags(userId);
+            if ((oldFlags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) == 0
+                    && (newFlags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
+                if (mPermissionReviewRequired == null) {
+                    mPermissionReviewRequired = new SparseBooleanArray();
+                }
+                mPermissionReviewRequired.put(userId, true);
+            } else if ((oldFlags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0
+                    && (newFlags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) == 0) {
+                if (mPermissionReviewRequired != null) {
+                    mPermissionReviewRequired.delete(userId);
+                    if (mPermissionReviewRequired.size() <= 0) {
+                        mPermissionReviewRequired = null;
+                    }
+                }
+            }
+        }
+        return updated;
     }
 
     public boolean updatePermissionFlagsForAllPermissions(
@@ -430,6 +477,7 @@
     public void reset() {
         mGlobalGids = NO_GIDS;
         mPermissions = null;
+        mPermissionReviewRequired = null;
     }
 
     private PermissionState getPermissionState(String name, int userId) {
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 424b902..b31d731 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -652,6 +652,7 @@
     private void initDefaultGuestRestrictions() {
         synchronized (mGuestRestrictions) {
             if (mGuestRestrictions.isEmpty()) {
+                mGuestRestrictions.putBoolean(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, true);
                 mGuestRestrictions.putBoolean(UserManager.DISALLOW_OUTGOING_CALLS, true);
                 mGuestRestrictions.putBoolean(UserManager.DISALLOW_SMS, true);
             }
@@ -868,10 +869,8 @@
         mHandler.post(new Runnable() {
             @Override
             public void run() {
-                synchronized (mRestrictionsLock) {
-                    UserRestrictionsUtils.applyUserRestrictionsLR(
-                            mContext, userId, newRestrictionsFinal, prevRestrictionsFinal);
-                }
+                UserRestrictionsUtils.applyUserRestrictions(
+                        mContext, userId, newRestrictionsFinal, prevRestrictionsFinal);
 
                 final UserRestrictionsListener[] listeners;
                 synchronized (mUserRestrictionsListeners) {
@@ -1652,6 +1651,11 @@
             }
             updateUserIds();
             Bundle restrictions = new Bundle();
+            if (isGuest) {
+                synchronized (mGuestRestrictions) {
+                    restrictions.putAll(mGuestRestrictions);
+                }
+            }
             synchronized (mRestrictionsLock) {
                 mBaseUserRestrictions.append(userId, restrictions);
             }
@@ -1805,8 +1809,8 @@
             if (DBG) Slog.i(LOG_TAG, "Stopping user " + userHandle);
             int res;
             try {
-                res = ActivityManagerNative.getDefault().stopUser(userHandle,
-                        new IStopUserCallback.Stub() {
+                res = ActivityManagerNative.getDefault().stopUser(userHandle, /* force= */ true,
+                new IStopUserCallback.Stub() {
                             @Override
                             public void userStopped(int userId) {
                                 finishRemoveUser(userId);
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index 77abd3e..816903e 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -22,11 +22,14 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.app.ActivityManagerNative;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Bundle;
+import android.os.RemoteException;
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -84,7 +87,8 @@
             UserManager.DISALLOW_SAFE_BOOT,
             UserManager.ALLOW_PARENT_PROFILE_APP_LINKING,
             UserManager.DISALLOW_RECORD_AUDIO,
-            UserManager.DISALLOW_CAMERA
+            UserManager.DISALLOW_CAMERA,
+            UserManager.DISALLOW_RUN_IN_BACKGROUND
     );
 
     /**
@@ -126,6 +130,7 @@
      */
     private static final Set<String> GLOBAL_RESTRICTIONS = Sets.newArraySet(
             UserManager.DISALLOW_ADJUST_VOLUME,
+            UserManager.DISALLOW_RUN_IN_BACKGROUND,
             UserManager.DISALLOW_UNMUTE_MICROPHONE
     );
 
@@ -263,34 +268,28 @@
      * Takes a new use restriction set and the previous set, and apply the restrictions that have
      * changed.
      *
-     * <p>Note this method is called by {@link UserManagerService} while holding
-     * {@code mRestrictionLock}. Be aware when calling into other services, which could cause
-     * a deadlock.
+     * <p>Note this method is called by {@link UserManagerService} without holding any locks.
      */
-    public static void applyUserRestrictionsLR(Context context, int userId,
+    public static void applyUserRestrictions(Context context, int userId,
             Bundle newRestrictions, Bundle prevRestrictions) {
         for (String key : USER_RESTRICTIONS) {
             final boolean newValue = newRestrictions.getBoolean(key);
             final boolean prevValue = prevRestrictions.getBoolean(key);
 
             if (newValue != prevValue) {
-                applyUserRestrictionLR(context, userId, key, newValue);
+                applyUserRestriction(context, userId, key, newValue);
             }
         }
     }
-    
+
     /**
      * Apply each user restriction.
      *
-     * <p>Note this method is called by {@link UserManagerService} while holding
-     * {@code mRestrictionLock}. Be aware when calling into other services, which could cause
-     * a deadlock.
-     *
      * <p>See also {@link
      * com.android.providers.settings.SettingsProvider#isGlobalOrSecureSettingRestrictedForUser},
      * which should be in sync with this method.
      */
-    private static void applyUserRestrictionLR(Context context, int userId, String key,
+    private static void applyUserRestriction(Context context, int userId, String key,
             boolean newValue) {
         if (UserManagerService.DBG) {
             Log.d(TAG, "Applying user restriction: userId=" + userId
@@ -365,6 +364,17 @@
                                 userId);
                     }
                     break;
+                case UserManager.DISALLOW_RUN_IN_BACKGROUND:
+                    if (newValue) {
+                        int currentUser = ActivityManager.getCurrentUser();
+                        if (currentUser != userId && userId != UserHandle.USER_SYSTEM) {
+                            try {
+                                ActivityManagerNative.getDefault().stopUser(userId, false, null);
+                            } catch (RemoteException e) {
+                                throw e.rethrowAsRuntimeException();
+                            }
+                        }
+                    }
             }
         } finally {
             Binder.restoreCallingIdentity(id);
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 77a29f3..3d00e02 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -257,7 +257,7 @@
             if (win.mAttrs.type == WindowManager.LayoutParams.TYPE_BASE_APPLICATION
                     || win.mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_STARTING) {
                 // In cases where there are multiple windows, we prefer the non-exiting window. This
-                // happens for example when when replacing windows during an activity relaunch. When
+                // happens for example when replacing windows during an activity relaunch. When
                 // constructing the animation, we want the new window, not the exiting one.
                 if (win.mExiting) {
                     candidate = win;
@@ -269,6 +269,10 @@
         return candidate;
     }
 
+    boolean stackCanReceiveKeys() {
+        return (windows.size() > 0) ? windows.get(windows.size() - 1).stackCanReceiveKeys() : false;
+    }
+
     boolean isVisible() {
         final int N = allAppWindows.size();
         for (int i=0; i<N; i++) {
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index e9e09ec..4bbf586 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -94,8 +94,9 @@
     Region mNonResizeableRegion = new Region();
 
     /** Save allocating when calculating rects */
-    private Rect mTmpRect = new Rect();
-    private Rect mTmpRect2 = new Rect();
+    private final Rect mTmpRect = new Rect();
+    private final Rect mTmpRect2 = new Rect();
+    private final Region mTmpRegion = new Region();
 
     /** For gathering Task objects in order. */
     final ArrayList<Task> mTmpTaskHistory = new ArrayList<Task>();
@@ -403,6 +404,14 @@
         if (addBackFocusedTask) {
             mTouchExcludeRegion.op(mTmpRect2, Region.Op.UNION);
         }
+        final WindowState inputMethod = mService.mInputMethodWindow;
+        if (inputMethod != null && inputMethod.isVisibleLw()) {
+            // If the input method is visible and the user is typing, we don't want these touch
+            // events to be intercepted and used to change focus. This would likely cause a
+            // disappearance of the input method.
+            inputMethod.getTouchableRegion(mTmpRegion);
+            mTouchExcludeRegion.op(mTmpRegion, Region.Op.UNION);
+        }
         if (mTapDetector != null) {
             mTapDetector.setTouchExcludeRegion(mTouchExcludeRegion, mNonResizeableRegion);
         }
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 74e8e53..22f1d63 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -26,6 +26,11 @@
 import static com.android.server.wm.WindowManagerService.DEBUG_RESIZE;
 import static com.android.server.wm.WindowManagerService.DEBUG_STACK;
 import static com.android.server.wm.WindowManagerService.H.RESIZE_TASK;
+import static com.android.server.wm.WindowManagerService.H.SHOW_NON_RESIZEABLE_DOCK_TOAST;
+import static android.view.WindowManager.DOCKED_INVALID;
+import static android.view.WindowManager.DOCKED_LEFT;
+import static android.view.WindowManager.DOCKED_RIGHT;
+import static android.view.WindowManager.DOCKED_TOP;
 
 import android.app.ActivityManager.StackId;
 import android.content.res.Configuration;
@@ -76,6 +81,11 @@
     // Whether the task is resizeable
     private boolean mResizeable;
 
+    // Whether we need to show toast about the app being non-resizeable when it becomes visible.
+    // This flag is set when a non-resizeable task is docked (or side-by-side). It's cleared
+    // after we show the toast.
+    private boolean mShowNonResizeableDockToast;
+
     // Whether the task is currently being drag-resized
     private boolean mDragResizing;
 
@@ -92,6 +102,41 @@
         return mStack.getDisplayContent();
     }
 
+    void setShowNonResizeableDockToast() {
+        mShowNonResizeableDockToast = true;
+    }
+
+    void scheduleShowNonResizeableDockToastIfNeeded() {
+        if (!mShowNonResizeableDockToast) {
+            return;
+        }
+        final DisplayContent displayContent = mStack.getDisplayContent();
+        // If docked stack is not yet visible, we don't want to show the toast yet,
+        // since we need the visible rect of the docked task to position the toast.
+        if (displayContent == null || displayContent.getDockedStackLocked() == null) {
+            return;
+        }
+
+        mShowNonResizeableDockToast = false;
+
+        final int dockSide = mStack.getDockSide();
+        int xOffset = 0;
+        int yOffset = 0;
+        if (dockSide != DOCKED_INVALID) {
+            mStack.getBounds(mTmpRect);
+            displayContent.getLogicalDisplayRect(mTmpRect2);
+
+            if (dockSide == DOCKED_LEFT || dockSide == DOCKED_RIGHT) {
+                xOffset = mTmpRect.centerX() - mTmpRect2.centerX();
+            } else if (dockSide == DOCKED_TOP) {
+                // We don't adjust for DOCKED_BOTTOM case since it's already at the bottom.
+                yOffset = mTmpRect2.bottom - mTmpRect.bottom;
+            }
+            mService.mH.obtainMessage(
+                    SHOW_NON_RESIZEABLE_DOCK_TOAST, xOffset, yOffset).sendToTarget();
+        }
+    }
+
     void addAppToken(int addPos, AppWindowToken wtoken) {
         final int lastPos = mAppTokens.size();
         if (addPos >= lastPos) {
@@ -517,8 +562,10 @@
     }
 
     public void printTo(String prefix, PrintWriter pw) {
-        pw.print(prefix); pw.print("taskId="); pw.print(mTaskId);
-                pw.print(prefix); pw.print("appTokens="); pw.print(mAppTokens);
-                pw.print(prefix); pw.print("mdr="); pw.println(mDeferRemoval);
+        pw.print(prefix); pw.print("taskId="); pw.println(mTaskId);
+            pw.print(prefix + prefix); pw.print("mFullscreen="); pw.println(mFullscreen);
+            pw.print(prefix + prefix); pw.print("mBounds="); pw.println(mBounds.toShortString());
+            pw.print(prefix + prefix); pw.print("mdr="); pw.println(mDeferRemoval);
+            pw.print(prefix + prefix); pw.print("appTokens="); pw.println(mAppTokens);
     }
 }
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 87deaa4..611ad40 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -602,6 +602,8 @@
     public void dump(String prefix, PrintWriter pw) {
         pw.print(prefix); pw.print("mStackId="); pw.println(mStackId);
         pw.print(prefix); pw.print("mDeferDetach="); pw.println(mDeferDetach);
+        pw.print(prefix); pw.print("mFullscreen="); pw.println(mFullscreen);
+        pw.print(prefix); pw.print("mBounds="); pw.println(mBounds.toShortString());
         for (int taskNdx = 0; taskNdx < mTasks.size(); ++taskNdx) {
             pw.print(prefix);
             mTasks.get(taskNdx).printTo(prefix + " ", pw);
@@ -658,10 +660,11 @@
     }
 
     /**
-     * For docked workspace provides information which side of the screen was the dock anchored.
+     * For docked workspace (or workspace that's side-by-side to the docked), provides
+     * information which side of the screen was the dock anchored.
      */
     int getDockSide() {
-        if (mStackId != DOCKED_STACK_ID) {
+        if (mStackId != DOCKED_STACK_ID && !StackId.isResizeableByDockedStack(mStackId)) {
             return DOCKED_INVALID;
         }
         if (mDisplayContent == null) {
@@ -699,26 +702,4 @@
         }
         return false;
     }
-
-    /**
-     * Returns true if this stack has a window that is fully visible, doesn't perform an entry
-     * animation and is just positioned where it's supposed to be.
-     */
-    boolean hasWindowWithFinalVisibility() {
-        for (int i = mTasks.size() - 1; i >= 0; i--) {
-            Task task = mTasks.get(i);
-            for (int j = task.mAppTokens.size() - 1; j >= 0; j--) {
-                final AppWindowToken token = task.mAppTokens.get(j);
-                if (token.mAppAnimator.animating || token.mWillReplaceWindow) {
-                    continue;
-                }
-                WindowState win = token.findMainWindow();
-                if (win != null && !win.mWinAnimator.mEnterAnimationPending
-                        && !win.mWinAnimator.mEnteringAnimation) {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
 }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 66bc98e..707450c 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -150,8 +150,10 @@
 import android.view.WindowManagerPolicy;
 import android.view.WindowManagerPolicy.PointerEventListener;
 import android.view.animation.Animation;
+import android.widget.Toast;
 
 import com.android.internal.app.IAssistScreenshotReceiver;
+import com.android.internal.R;
 import com.android.internal.util.FastPrintWriter;
 import com.android.internal.view.IInputContext;
 import com.android.internal.view.IInputMethodClient;
@@ -2526,10 +2528,12 @@
                             + "attached to a parent win=" + win);
                 }
 
-                win.mFrame.left = left;
-                win.mFrame.top = top;
-                win.mFrame.right = right;
-                win.mFrame.bottom = bottom;
+                win.mAttrs.x = left;
+                win.mAttrs.y = top;
+                win.mAttrs.width = right - left;
+                win.mAttrs.height = bottom - top;
+
+                win.setWindowScale(win.mRequestedWidth, win.mRequestedHeight);
 
                 win.mWinAnimator.computeShownFrameLocked();
 
@@ -4787,6 +4791,17 @@
         }
     }
 
+    public void scheduleShowNonResizeableDockToast(int taskId) {
+        synchronized (mWindowMap) {
+            Task task = mTaskIdToTask.get(taskId);
+            if (task == null) {
+                if (DEBUG_STACK) Slog.i(TAG, "scheduleShowToast: could not find taskId=" + taskId);
+                return;
+            }
+            task.setShowNonResizeableDockToast();
+        }
+    }
+
     public void getStackBounds(int stackId, Rect bounds) {
         synchronized (mWindowMap) {
             final TaskStack stack = mStackIdToStack.get(stackId);
@@ -7454,6 +7469,7 @@
         public static final int RESIZE_TASK = 44;
 
         public static final int TWO_FINGER_SCROLL_START = 45;
+        public static final int SHOW_NON_RESIZEABLE_DOCK_TOAST = 46;
 
         /**
          * Used to denote that an integer field in a message will not be used.
@@ -8026,6 +8042,17 @@
                     }
                 }
                 break;
+                case SHOW_NON_RESIZEABLE_DOCK_TOAST: {
+                    final Toast toast = Toast.makeText(mContext,
+                            mContext.getString(R.string.dock_non_resizeble_text),
+                            Toast.LENGTH_LONG);
+                    final int gravity = toast.getGravity();
+                    final int xOffset = toast.getXOffset() + msg.arg1;
+                    final int yOffset = toast.getYOffset() + msg.arg2;
+                    toast.setGravity(gravity, xOffset, yOffset);
+                    toast.show();
+                }
+                break;
             }
             if (DEBUG_WINDOW_TRACE) {
                 Slog.v(TAG, "handleMessage: exit");
@@ -9106,8 +9133,9 @@
                         if (wtoken == token) {
                             break;
                         }
-                        if (mFocusedApp == token) {
-                            // Whoops, we are below the focused app...  no focus for you!
+                        if (mFocusedApp == token && token.stackCanReceiveKeys()) {
+                            // Whoops, we are below the focused app whose stack can receive keys...
+                            // No focus for you!!!
                             if (localLOGV || DEBUG_FOCUS_LIGHT) Slog.v(TAG,
                                     "findFocusedWindow: Reached focused app=" + mFocusedApp);
                             return null;
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 29cadf3..cfa2bb3 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -56,6 +56,7 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 
+import static android.app.ActivityManager.StackId;
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
 import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT;
 import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME;
@@ -1564,13 +1565,17 @@
         }
     }
 
-    /**
-     * @return true if this window desires key events.
-     */
-    public final boolean canReceiveKeys() {
+    /** @return true if this window desires key events. */
+    boolean canReceiveKeys() {
         return isVisibleOrAdding()
                 && (mViewVisibility == View.VISIBLE)
-                && ((mAttrs.flags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) == 0);
+                && ((mAttrs.flags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) == 0)
+                && stackCanReceiveKeys();
+    }
+
+    boolean stackCanReceiveKeys() {
+        final TaskStack stack = getStack();
+        return stack != null && StackId.canReceiveKeys(stack.mStackId);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 93c2ff6..132b1b6 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -1474,6 +1474,11 @@
                 }
                 mWin.mAppToken.updateReportedVisibilityLocked();
             }
+
+            final Task task = mWin.getTask();
+            if (task != null) {
+                task.scheduleShowNonResizeableDockToastIfNeeded();
+            }
             return true;
         }
         return false;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index d7afdf4..dbfd80d 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -3855,10 +3855,7 @@
         synchronized (this) {
             ActiveAdmin admin = getActiveAdminUncheckedLocked(comp, userHandle);
             if (admin == null) {
-                try {
-                    result.sendResult(null);
-                } catch (RemoteException e) {
-                }
+                result.sendResult(null);
                 return;
             }
             Intent intent = new Intent(DeviceAdminReceiver.ACTION_DEVICE_ADMIN_DISABLE_REQUESTED);
@@ -3868,10 +3865,7 @@
                     null, new BroadcastReceiver() {
                 @Override
                 public void onReceive(Context context, Intent intent) {
-                    try {
-                        result.sendResult(getResultExtras(false));
-                    } catch (RemoteException e) {
-                    }
+                    result.sendResult(getResultExtras(false));
                 }
             }, null, Activity.RESULT_OK, null, null);
         }
@@ -3891,39 +3885,31 @@
 
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.BIND_DEVICE_ADMIN, null);
-        DevicePolicyData p = getUserData(userHandle);
-
         validateQualityConstant(quality);
 
-        synchronized (this) {
-            if (p.mActivePasswordQuality != quality || p.mActivePasswordLength != length
-                    || p.mFailedPasswordAttempts != 0 || p.mActivePasswordLetters != letters
-                    || p.mActivePasswordUpperCase != uppercase
-                    || p.mActivePasswordLowerCase != lowercase
-                    || p.mActivePasswordNumeric != numbers
-                    || p.mActivePasswordSymbols != symbols
-                    || p.mActivePasswordNonLetter != nonletter) {
-                long ident = mInjector.binderClearCallingIdentity();
-                try {
-                    p.mActivePasswordQuality = quality;
-                    p.mActivePasswordLength = length;
-                    p.mActivePasswordLetters = letters;
-                    p.mActivePasswordLowerCase = lowercase;
-                    p.mActivePasswordUpperCase = uppercase;
-                    p.mActivePasswordNumeric = numbers;
-                    p.mActivePasswordSymbols = symbols;
-                    p.mActivePasswordNonLetter = nonletter;
-                    p.mFailedPasswordAttempts = 0;
-                    saveSettingsLocked(userHandle);
-                    updatePasswordExpirationsLocked(userHandle);
-                    setExpirationAlarmCheckLocked(mContext, p);
-                    sendAdminCommandToSelfAndProfilesLocked(
-                            DeviceAdminReceiver.ACTION_PASSWORD_CHANGED,
-                            DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, userHandle);
-                } finally {
-                    mInjector.binderRestoreCallingIdentity(ident);
-                }
+        DevicePolicyData policy = getUserData(userHandle);
+
+        long ident = mInjector.binderClearCallingIdentity();
+        try {
+            synchronized (this) {
+                policy.mActivePasswordQuality = quality;
+                policy.mActivePasswordLength = length;
+                policy.mActivePasswordLetters = letters;
+                policy.mActivePasswordLowerCase = lowercase;
+                policy.mActivePasswordUpperCase = uppercase;
+                policy.mActivePasswordNumeric = numbers;
+                policy.mActivePasswordSymbols = symbols;
+                policy.mActivePasswordNonLetter = nonletter;
+                policy.mFailedPasswordAttempts = 0;
+                saveSettingsLocked(userHandle);
+                updatePasswordExpirationsLocked(userHandle);
+                setExpirationAlarmCheckLocked(mContext, policy);
+                sendAdminCommandToSelfAndProfilesLocked(
+                        DeviceAdminReceiver.ACTION_PASSWORD_CHANGED,
+                        DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, userHandle);
             }
+        } finally {
+            mInjector.binderRestoreCallingIdentity(ident);
         }
     }
 
diff --git a/services/print/java/com/android/server/print/RemotePrintService.java b/services/print/java/com/android/server/print/RemotePrintService.java
index 0ab1657..77a47f8 100644
--- a/services/print/java/com/android/server/print/RemotePrintService.java
+++ b/services/print/java/com/android/server/print/RemotePrintService.java
@@ -16,6 +16,9 @@
 
 package com.android.server.print;
 
+import android.annotation.FloatRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -757,6 +760,33 @@
         }
 
         @Override
+        public void setProgress(@NonNull PrintJobId printJobId,
+                @FloatRange(from=0.0, to=1.0) float progress) {
+            RemotePrintService service = mWeakService.get();
+            if (service != null) {
+                final long identity = Binder.clearCallingIdentity();
+                try {
+                    service.mSpooler.setProgress(printJobId, progress);
+                } finally {
+                    Binder.restoreCallingIdentity(identity);
+                }
+            }
+        }
+
+        @Override
+        public void setStatus(@NonNull PrintJobId printJobId, @Nullable CharSequence status) {
+            RemotePrintService service = mWeakService.get();
+            if (service != null) {
+                final long identity = Binder.clearCallingIdentity();
+                try {
+                    service.mSpooler.setStatus(printJobId, status);
+                } finally {
+                    Binder.restoreCallingIdentity(identity);
+                }
+            }
+        }
+
+        @Override
         @SuppressWarnings({"rawtypes", "unchecked"})
         public void onPrintersAdded(ParceledListSlice printers) {
             RemotePrintService service = mWeakService.get();
diff --git a/services/print/java/com/android/server/print/RemotePrintSpooler.java b/services/print/java/com/android/server/print/RemotePrintSpooler.java
index e5370f4..c506b6f 100644
--- a/services/print/java/com/android/server/print/RemotePrintSpooler.java
+++ b/services/print/java/com/android/server/print/RemotePrintSpooler.java
@@ -16,6 +16,9 @@
 
 package com.android.server.print;
 
+import android.annotation.FloatRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -229,6 +232,61 @@
         return false;
     }
 
+    /**
+     * Set progress of a print job.
+     *
+     * @param printJobId The print job to update
+     * @param progress The new progress
+     */
+    public final void setProgress(@NonNull PrintJobId printJobId,
+            @FloatRange(from=0.0, to=1.0) float progress) {
+        throwIfCalledOnMainThread();
+        synchronized (mLock) {
+            throwIfDestroyedLocked();
+            mCanUnbind = false;
+        }
+        try {
+            getRemoteInstanceLazy().setProgress(printJobId, progress);
+        } catch (RemoteException|TimeoutException re) {
+            Slog.e(LOG_TAG, "Error setting progress.", re);
+        } finally {
+            if (DEBUG) {
+                Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] setProgress()");
+            }
+            synchronized (mLock) {
+                mCanUnbind = true;
+                mLock.notifyAll();
+            }
+        }
+    }
+
+    /**
+     * Set status of a print job.
+     *
+     * @param printJobId The print job to update
+     * @param status The new status
+     */
+    public final void setStatus(@NonNull PrintJobId printJobId, @Nullable CharSequence status) {
+        throwIfCalledOnMainThread();
+        synchronized (mLock) {
+            throwIfDestroyedLocked();
+            mCanUnbind = false;
+        }
+        try {
+            getRemoteInstanceLazy().setStatus(printJobId, status);
+        } catch (RemoteException|TimeoutException re) {
+            Slog.e(LOG_TAG, "Error setting status.", re);
+        } finally {
+            if (DEBUG) {
+                Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] setStatus()");
+            }
+            synchronized (mLock) {
+                mCanUnbind = true;
+                mLock.notifyAll();
+            }
+        }
+    }
+
     public final boolean setPrintJobTag(PrintJobId printJobId, String tag) {
         throwIfCalledOnMainThread();
         synchronized (mLock) {
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
index 66c7dbb..b4bca3e 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -31,7 +31,8 @@
 
 /** Test {@link UserManager} functionality. */
 public class UserManagerTest extends AndroidTestCase {
-
+    private static final int REMOVE_CHECK_INTERVAL = 500;
+    private static final int REMOVE_TIMEOUT = 60 * 1000;
     private UserManager mUserManager = null;
     private final Object mUserLock = new Object();
     private List<Integer> usersToRemove;
@@ -227,10 +228,16 @@
     private void removeUser(int userId) {
         synchronized (mUserLock) {
             mUserManager.removeUser(userId);
+            long time = System.currentTimeMillis();
             while (mUserManager.getUserInfo(userId) != null) {
                 try {
-                    mUserLock.wait(500);
+                    mUserLock.wait(REMOVE_CHECK_INTERVAL);
                 } catch (InterruptedException ie) {
+                    Thread.currentThread().interrupt();
+                    return;
+                }
+                if (System.currentTimeMillis() - time > REMOVE_TIMEOUT) {
+                    fail("Timeout waiting for removeUser. userId = " + userId);
                 }
             }
         }
diff --git a/telephony/java/android/telephony/CellLocation.java b/telephony/java/android/telephony/CellLocation.java
index f9a222f..5bcaa6e 100644
--- a/telephony/java/android/telephony/CellLocation.java
+++ b/telephony/java/android/telephony/CellLocation.java
@@ -81,6 +81,12 @@
     public abstract boolean isEmpty();
 
     /**
+     * Invalidate this object.  The location area code and the cell id are set to -1.
+     * @hide
+     */
+    public abstract void setStateInvalid();
+
+    /**
      * Return a new CellLocation object representing an unknown
      * location, or null for unknown/none phone radio types.
      *
diff --git a/telephony/java/android/telephony/cdma/CdmaCellLocation.java b/telephony/java/android/telephony/cdma/CdmaCellLocation.java
index 6cfae6a..7c10569 100644
--- a/telephony/java/android/telephony/cdma/CdmaCellLocation.java
+++ b/telephony/java/android/telephony/cdma/CdmaCellLocation.java
@@ -123,6 +123,7 @@
     /**
      * Invalidate this object.  The cell location data is set to invalid values.
      */
+    @Override
     public void setStateInvalid() {
         this.mBaseStationId = -1;
         this.mBaseStationLatitude = INVALID_LAT_LONG;
@@ -134,7 +135,7 @@
     /**
      * Set the cell location data.
      */
-     public void setCellLocationData(int baseStationId, int baseStationLatitude,
+    public void setCellLocationData(int baseStationId, int baseStationLatitude,
          int baseStationLongitude) {
          // The following values have to be written in the correct sequence
          this.mBaseStationId = baseStationId;
diff --git a/telephony/java/android/telephony/gsm/GsmCellLocation.java b/telephony/java/android/telephony/gsm/GsmCellLocation.java
index a3889b2..1717802 100644
--- a/telephony/java/android/telephony/gsm/GsmCellLocation.java
+++ b/telephony/java/android/telephony/gsm/GsmCellLocation.java
@@ -72,6 +72,7 @@
     /**
      * Invalidate this object.  The location area code and the cell id are set to -1.
      */
+    @Override
     public void setStateInvalid() {
         mLac = -1;
         mCid = -1;
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/MultiProducerActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/MultiProducerActivity.java
index 7628c5c..63fa3f9 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/MultiProducerActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/MultiProducerActivity.java
@@ -25,7 +25,7 @@
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
 import android.view.DisplayListCanvas;
-import android.view.HardwareRenderer;
+import android.view.ThreadedRenderer;
 import android.view.RenderNode;
 import android.view.ThreadedRenderer;
 import android.view.View;
@@ -120,7 +120,7 @@
             if (view == null) {
                 view.postDelayed(mSetup, 50);
             }
-            HardwareRenderer renderer = view.getHardwareRenderer();
+            ThreadedRenderer renderer = view.getHardwareRenderer();
             if (renderer == null || view.getWidth() == 0) {
                 view.postDelayed(mSetup, 50);
             }
diff --git a/tests/NetworkSecurityConfigTest/res/xml/override_dedup.xml b/tests/NetworkSecurityConfigTest/res/xml/override_dedup.xml
new file mode 100644
index 0000000..5ba5675
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/res/xml/override_dedup.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<network-security-config>
+  <!-- Entry with a bad pin. Connections to this will only succeed if overridePins is set. -->
+  <domain-config>
+    <domain>android.com</domain>
+    <pin-set>
+      <pin digest="SHA-256">aaaaaaaaIAq2Y49orFOOQKurWxmmSFZhBCoQYcRhJ3Y=</pin>
+    </pin-set>
+    <trust-anchors>
+      <certificates src="system" overridePins="false" />
+    </trust-anchors>
+  </domain-config>
+  <!-- override that contains all of the system CA store. This should completely override the
+       anchors in the domain config-above with ones that have overridePins set. -->
+  <debug-overrides>
+    <trust-anchors>
+      <certificates src="system" />
+    </trust-anchors>
+  </debug-overrides>
+</network-security-config>
diff --git a/tests/NetworkSecurityConfigTest/src/android/security/net/config/TestCertificateSource.java b/tests/NetworkSecurityConfigTest/src/android/security/net/config/TestCertificateSource.java
index 92eadc0..69b2a9d 100644
--- a/tests/NetworkSecurityConfigTest/src/android/security/net/config/TestCertificateSource.java
+++ b/tests/NetworkSecurityConfigTest/src/android/security/net/config/TestCertificateSource.java
@@ -19,15 +19,29 @@
 import java.util.Set;
 import java.security.cert.X509Certificate;
 
+import com.android.org.conscrypt.TrustedCertificateIndex;
+
 /** @hide */
 public class TestCertificateSource implements CertificateSource {
 
     private final Set<X509Certificate> mCertificates;
+    private final TrustedCertificateIndex mIndex = new TrustedCertificateIndex();
     public TestCertificateSource(Set<X509Certificate> certificates) {
         mCertificates = certificates;
+        for (X509Certificate cert : certificates) {
+            mIndex.index(cert);
+        }
     }
 
     public Set<X509Certificate> getCertificates() {
             return mCertificates;
     }
+
+    public X509Certificate findBySubjectAndPublicKey(X509Certificate cert) {
+        java.security.cert.TrustAnchor anchor = mIndex.findBySubjectAndPublicKey(cert);
+        if (anchor == null) {
+            return null;
+        }
+        return anchor.getTrustedCert();
+    }
 }
diff --git a/tests/NetworkSecurityConfigTest/src/android/security/net/config/XmlConfigTests.java b/tests/NetworkSecurityConfigTest/src/android/security/net/config/XmlConfigTests.java
index c6f3680..998bb68 100644
--- a/tests/NetworkSecurityConfigTest/src/android/security/net/config/XmlConfigTests.java
+++ b/tests/NetworkSecurityConfigTest/src/android/security/net/config/XmlConfigTests.java
@@ -402,4 +402,22 @@
         context.init(null, tms, null);
         TestUtils.assertConnectionSucceeds(context, "android.com" , 443);
     }
+
+    public void testDebugDedup() throws Exception {
+        XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.override_dedup, true);
+        ApplicationConfig appConfig = new ApplicationConfig(source);
+        assertTrue(appConfig.hasPerDomainConfigs());
+        // Check android.com.
+        NetworkSecurityConfig config = appConfig.getConfigForHostname("android.com");
+        PinSet pinSet = config.getPins();
+        assertFalse(pinSet.pins.isEmpty());
+        // Check that all TrustAnchors come from the override pins debug source.
+        for (TrustAnchor anchor : config.getTrustAnchors()) {
+            assertTrue(anchor.overridesPins);
+        }
+        // Try connections.
+        SSLContext context = TestUtils.getSSLContext(source);
+        TestUtils.assertConnectionSucceeds(context, "android.com", 443);
+        TestUtils.assertUrlConnectionSucceeds(context, "android.com", 443);
+    }
 }
diff --git a/tests/RenderThreadTest/src/com/example/renderthread/MainActivity.java b/tests/RenderThreadTest/src/com/example/renderthread/MainActivity.java
index fc5426c..ee4c834 100644
--- a/tests/RenderThreadTest/src/com/example/renderthread/MainActivity.java
+++ b/tests/RenderThreadTest/src/com/example/renderthread/MainActivity.java
@@ -6,7 +6,6 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.util.Log;
-import android.view.HardwareRenderer;
 import android.view.RenderNodeAnimator;
 import android.view.View;
 import android.widget.AdapterView;
diff --git a/tools/aapt/Package.cpp b/tools/aapt/Package.cpp
index cb244ec..641c34b 100644
--- a/tools/aapt/Package.cpp
+++ b/tools/aapt/Package.cpp
@@ -33,7 +33,7 @@
     ".mpg", ".mpeg", ".mid", ".midi", ".smf", ".jet",
     ".rtttl", ".imy", ".xmf", ".mp4", ".m4a",
     ".m4v", ".3gp", ".3gpp", ".3g2", ".3gpp2",
-    ".amr", ".awb", ".wma", ".wmv"
+    ".amr", ".awb", ".wma", ".wmv", ".webm"
 };
 
 /* fwd decls, so I can write this downward */
diff --git a/tools/layoutlib/bridge/src/android/animation/FakeAnimator.java b/tools/layoutlib/bridge/src/android/animation/FakeAnimator.java
deleted file mode 100644
index 78aedc5..0000000
--- a/tools/layoutlib/bridge/src/android/animation/FakeAnimator.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.animation;
-
-/**
- * A fake implementation of Animator which doesn't do anything.
- */
-public class FakeAnimator extends Animator {
-    @Override
-    public long getStartDelay() {
-        return 0;
-    }
-
-    @Override
-    public void setStartDelay(long startDelay) {
-
-    }
-
-    @Override
-    public Animator setDuration(long duration) {
-        return this;
-    }
-
-    @Override
-    public long getDuration() {
-        return 0;
-    }
-
-    @Override
-    public void setInterpolator(TimeInterpolator value) {
-
-    }
-
-    @Override
-    public boolean isRunning() {
-        return false;
-    }
-}
diff --git a/tools/layoutlib/bridge/src/android/animation/PropertyValuesHolder_Delegate.java b/tools/layoutlib/bridge/src/android/animation/PropertyValuesHolder_Delegate.java
index 4603b63..54021c9 100644
--- a/tools/layoutlib/bridge/src/android/animation/PropertyValuesHolder_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/animation/PropertyValuesHolder_Delegate.java
@@ -16,9 +16,16 @@
 
 package android.animation;
 
+import com.android.layoutlib.bridge.Bridge;
 import com.android.layoutlib.bridge.impl.DelegateManager;
 import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
 /**
  * Delegate implementing the native methods of android.animation.PropertyValuesHolder
  *
@@ -29,81 +36,161 @@
  * around to map int to instance of the delegate.
  *
  * The main goal of this class' methods are to provide a native way to access setters and getters
- * on some object. In this case we want to default to using Java reflection instead so the native
- * methods do nothing.
+ * on some object. We override these methods to use reflection since the original reflection
+ * implementation of the PropertyValuesHolder won't be able to access protected methods.
  *
  */
-/*package*/ class PropertyValuesHolder_Delegate {
+/*package*/
+@SuppressWarnings("unused")
+class PropertyValuesHolder_Delegate {
+    // This code is copied from android.animation.PropertyValuesHolder and must be kept in sync
+    // We try several different types when searching for appropriate setter/getter functions.
+    // The caller may have supplied values in a type that does not match the setter/getter
+    // functions (such as the integers 0 and 1 to represent floating point values for alpha).
+    // Also, the use of generics in constructors means that we end up with the Object versions
+    // of primitive types (Float vs. float). But most likely, the setter/getter functions
+    // will take primitive types instead.
+    // So we supply an ordered array of other types to try before giving up.
+    private static Class[] FLOAT_VARIANTS = {float.class, Float.class, double.class, int.class,
+            Double.class, Integer.class};
+    private static Class[] INTEGER_VARIANTS = {int.class, Integer.class, float.class, double.class,
+            Float.class, Double.class};
+
+    private static final Object sMethodIndexLock = new Object();
+    private static final Map<Long, Method> ID_TO_METHOD = new HashMap<Long, Method>();
+    private static final Map<String, Long> METHOD_NAME_TO_ID = new HashMap<String, Long>();
+    private static long sNextId = 1;
+
+    private static long registerMethod(Class<?> targetClass, String methodName, Class[] types,
+            int nArgs) {
+        // Encode the number of arguments in the method name
+        String methodIndexName = String.format("%1$s#%2$d", methodName, nArgs);
+        synchronized (sMethodIndexLock) {
+            Long methodId = METHOD_NAME_TO_ID.get(methodIndexName);
+
+            if (methodId != null) {
+                // The method was already registered
+                return methodId;
+            }
+
+            Class[] args = new Class[nArgs];
+            Method method = null;
+            for (Class typeVariant : types) {
+                for (int i = 0; i < nArgs; i++) {
+                    args[i] = typeVariant;
+                }
+                try {
+                    method = targetClass.getDeclaredMethod(methodName, args);
+                } catch (NoSuchMethodException ignore) {
+                }
+            }
+
+            if (method != null) {
+                methodId = sNextId++;
+                ID_TO_METHOD.put(methodId, method);
+                METHOD_NAME_TO_ID.put(methodIndexName, methodId);
+
+                return methodId;
+            }
+        }
+
+        // Method not found
+        return 0;
+    }
+
+    private static void callMethod(Object target, long methodID, Object... args) {
+        Method method = ID_TO_METHOD.get(methodID);
+        assert method != null;
+
+        try {
+            method.setAccessible(true);
+            method.invoke(target, args);
+        } catch (IllegalAccessException e) {
+            Bridge.getLog().error(null, "Unable to update property during animation", e, null);
+        } catch (InvocationTargetException e) {
+            Bridge.getLog().error(null, "Unable to update property during animation", e, null);
+        }
+    }
 
     @LayoutlibDelegate
     /*package*/ static long nGetIntMethod(Class<?> targetClass, String methodName) {
-        // return 0 to force PropertyValuesHolder to use Java reflection.
-        return 0;
+        return nGetMultipleIntMethod(targetClass, methodName, 1);
     }
 
     @LayoutlibDelegate
     /*package*/ static long nGetFloatMethod(Class<?> targetClass, String methodName) {
-        // return 0 to force PropertyValuesHolder to use Java reflection.
-        return 0;
+        return nGetMultipleFloatMethod(targetClass, methodName, 1);
     }
 
     @LayoutlibDelegate
     /*package*/ static long nGetMultipleIntMethod(Class<?> targetClass, String methodName,
             int numParams) {
-        // TODO: return the right thing.
-        return 0;
+        return registerMethod(targetClass, methodName, INTEGER_VARIANTS, numParams);
     }
 
     @LayoutlibDelegate
     /*package*/ static long nGetMultipleFloatMethod(Class<?> targetClass, String methodName,
             int numParams) {
-        // TODO: return the right thing.
-        return 0;
+        return registerMethod(targetClass, methodName, FLOAT_VARIANTS, numParams);
     }
 
     @LayoutlibDelegate
     /*package*/ static void nCallIntMethod(Object target, long methodID, int arg) {
-        // do nothing
+        callMethod(target, methodID, arg);
     }
 
     @LayoutlibDelegate
     /*package*/ static void nCallFloatMethod(Object target, long methodID, float arg) {
-        // do nothing
+        callMethod(target, methodID, arg);
     }
 
     @LayoutlibDelegate
     /*package*/ static void nCallTwoIntMethod(Object target, long methodID, int arg1,
             int arg2) {
-        // do nothing
+        callMethod(target, methodID, arg1, arg2);
     }
 
     @LayoutlibDelegate
     /*package*/ static void nCallFourIntMethod(Object target, long methodID, int arg1,
             int arg2, int arg3, int arg4) {
-        // do nothing
+        callMethod(target, methodID, arg1, arg2, arg3, arg4);
     }
 
     @LayoutlibDelegate
     /*package*/ static void nCallMultipleIntMethod(Object target, long methodID,
             int[] args) {
-        // do nothing
+        assert args != null;
+
+        // Box parameters
+        Object[] params = new Object[args.length];
+        for (int i = 0; i < args.length; i++) {
+            params[i] = args;
+        }
+        callMethod(target, methodID, params);
     }
 
     @LayoutlibDelegate
     /*package*/ static void nCallTwoFloatMethod(Object target, long methodID, float arg1,
             float arg2) {
-        // do nothing
+        callMethod(target, methodID, arg1, arg2);
     }
 
     @LayoutlibDelegate
     /*package*/ static void nCallFourFloatMethod(Object target, long methodID, float arg1,
             float arg2, float arg3, float arg4) {
-        // do nothing
+        callMethod(target, methodID, arg1, arg2, arg3, arg4);
     }
 
     @LayoutlibDelegate
     /*package*/ static void nCallMultipleFloatMethod(Object target, long methodID,
             float[] args) {
-        // do nothing
+        assert args != null;
+
+        // Box parameters
+        Object[] params = new Object[args.length];
+        for (int i = 0; i < args.length; i++) {
+            params[i] = args;
+        }
+        callMethod(target, methodID, params);
     }
 }
diff --git a/tools/layoutlib/bridge/src/android/graphics/PathMeasure_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/PathMeasure_Delegate.java
index dd2978f..3c71233 100644
--- a/tools/layoutlib/bridge/src/android/graphics/PathMeasure_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/PathMeasure_Delegate.java
@@ -44,7 +44,7 @@
 
     // ---- delegate data ----
     // This governs how accurate the approximation of the Path is.
-    private static final float PRECISION = 0.002f;
+    private static final float PRECISION = 0.0002f;
 
     /**
      * Array containing the path points components. There are three components for each point:
diff --git a/tools/layoutlib/bridge/src/android/os/SystemClock_Delegate.java b/tools/layoutlib/bridge/src/android/os/SystemClock_Delegate.java
index 5f0d98b..9677aaf 100644
--- a/tools/layoutlib/bridge/src/android/os/SystemClock_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/os/SystemClock_Delegate.java
@@ -18,6 +18,7 @@
 
 import com.android.layoutlib.bridge.impl.DelegateManager;
 import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+import com.android.tools.layoutlib.java.System_Delegate;
 
 /**
  * Delegate implementing the native methods of android.os.SystemClock
@@ -30,9 +31,6 @@
  *
  */
 public class SystemClock_Delegate {
-    private static long sBootTime = System.currentTimeMillis();
-    private static long sBootTimeNano = System.nanoTime();
-
     /**
      * Returns milliseconds since boot, not counting time spent in deep sleep.
      * <b>Note:</b> This value may get reset occasionally (before it would
@@ -42,7 +40,7 @@
      */
     @LayoutlibDelegate
     /*package*/ static long uptimeMillis() {
-        return System.currentTimeMillis() - sBootTime;
+        return System_Delegate.currentTimeMillis() - System_Delegate.bootTimeMillis();
     }
 
     /**
@@ -52,7 +50,7 @@
      */
     @LayoutlibDelegate
     /*package*/ static long elapsedRealtime() {
-        return System.currentTimeMillis() - sBootTime;
+        return System_Delegate.currentTimeMillis() - System_Delegate.bootTimeMillis();
     }
 
     /**
@@ -62,7 +60,7 @@
      */
     @LayoutlibDelegate
     /*package*/ static long elapsedRealtimeNanos() {
-        return System.nanoTime() - sBootTimeNano;
+        return System_Delegate.nanoTime() - System_Delegate.bootTime();
     }
 
     /**
@@ -72,7 +70,7 @@
      */
     @LayoutlibDelegate
     /*package*/ static long currentThreadTimeMillis() {
-        return System.currentTimeMillis();
+        return System_Delegate.currentTimeMillis();
     }
 
     /**
@@ -84,7 +82,7 @@
      */
     @LayoutlibDelegate
     /*package*/ static long currentThreadTimeMicro() {
-        return System.currentTimeMillis() * 1000;
+        return System_Delegate.currentTimeMillis() * 1000;
     }
 
     /**
diff --git a/tools/layoutlib/bridge/src/android/view/Choreographer_Delegate.java b/tools/layoutlib/bridge/src/android/view/Choreographer_Delegate.java
index f75ee50..01af669 100644
--- a/tools/layoutlib/bridge/src/android/view/Choreographer_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/view/Choreographer_Delegate.java
@@ -17,6 +17,8 @@
 
 import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 
+import java.util.concurrent.atomic.AtomicReference;
+
 /**
  * Delegate used to provide new implementation of a select few methods of {@link Choreographer}
  *
@@ -25,9 +27,41 @@
  *
  */
 public class Choreographer_Delegate {
+    static final AtomicReference<Choreographer> mInstance = new AtomicReference<Choreographer>();
+
+    @LayoutlibDelegate
+    public static Choreographer getInstance() {
+        if (mInstance.get() == null) {
+            mInstance.compareAndSet(null, Choreographer.getInstance_Original());
+        }
+
+        return mInstance.get();
+    }
 
     @LayoutlibDelegate
     public static float getRefreshRate() {
         return 60.f;
     }
+
+    @LayoutlibDelegate
+    static void scheduleVsyncLocked(Choreographer thisChoreographer) {
+        // do nothing
+    }
+
+    public static void doFrame(long frameTimeNanos) {
+        Choreographer thisChoreographer = Choreographer.getInstance();
+
+        thisChoreographer.mLastFrameTimeNanos = frameTimeNanos;
+
+        thisChoreographer.mFrameInfo.markInputHandlingStart();
+        thisChoreographer.doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);
+
+        thisChoreographer.mFrameInfo.markAnimationsStart();
+        thisChoreographer.doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);
+
+        thisChoreographer.mFrameInfo.markPerformTraversalsStart();
+        thisChoreographer.doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);
+
+        thisChoreographer.doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos);
+    }
 }
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
index 48ca7d8..683c4aa 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
@@ -183,7 +183,7 @@
      */
     private static LayoutLog sCurrentLog = sDefaultLog;
 
-    private static final int LAST_SUPPORTED_FEATURE = Features.RECYCLER_VIEW_ADAPTER;
+    private static final int LAST_SUPPORTED_FEATURE = Features.CHOREOGRAPHER;
 
     @Override
     public int getApiLevel() {
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java
index feb2590..2ac212c 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java
@@ -23,6 +23,7 @@
 import com.android.ide.common.rendering.api.Result;
 import com.android.ide.common.rendering.api.ViewInfo;
 import com.android.layoutlib.bridge.impl.RenderSessionImpl;
+import com.android.tools.layoutlib.java.System_Delegate;
 
 import android.view.View;
 import android.view.ViewGroup;
@@ -191,6 +192,21 @@
     }
 
     @Override
+    public void setSystemTimeNanos(long nanos) {
+        System_Delegate.setNanosTime(nanos);
+    }
+
+    @Override
+    public void setSystemBootTimeNanos(long nanos) {
+        System_Delegate.setBootTimeNanos(nanos);
+    }
+
+    @Override
+    public void setElapsedFrameTimeNanos(long nanos) {
+        mSession.setElapsedFrameTimeNanos(nanos);
+    }
+
+    @Override
     public void dispose() {
     }
 
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
index 0ffa357..ec50cfe 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
@@ -46,6 +46,7 @@
 import com.android.layoutlib.bridge.impl.binding.FakeAdapter;
 import com.android.layoutlib.bridge.impl.binding.FakeExpandableAdapter;
 import com.android.resources.ResourceType;
+import com.android.tools.layoutlib.java.System_Delegate;
 import com.android.util.Pair;
 
 import android.animation.AnimationThread;
@@ -62,6 +63,7 @@
 import android.preference.Preference_Delegate;
 import android.view.AttachInfo_Accessor;
 import android.view.BridgeInflater;
+import android.view.Choreographer_Delegate;
 import android.view.IWindowManager;
 import android.view.IWindowManagerImpl;
 import android.view.Surface;
@@ -120,6 +122,10 @@
     private int mMeasuredScreenWidth = -1;
     private int mMeasuredScreenHeight = -1;
     private boolean mIsAlphaChannelImage;
+    /** If >= 0, a frame will be executed */
+    private long mElapsedFrameTimeNanos = -1;
+    /** True if one frame has been already executed to start the animations */
+    private boolean mFirstFrameExecuted = false;
 
     // information being returned through the API
     private BufferedImage mImage;
@@ -252,6 +258,14 @@
     }
 
     /**
+     * Sets the time for which the next frame will be selected. The time is the elapsed time from
+     * the current system nanos time. You
+     */
+    public void setElapsedFrameTimeNanos(long nanos) {
+        mElapsedFrameTimeNanos = nanos;
+    }
+
+    /**
      * Renders the scene.
      * <p>
      * {@link #acquire(long)} must have been called before this.
@@ -428,6 +442,16 @@
                     gc.dispose();
                 }
 
+                if (mElapsedFrameTimeNanos >= 0) {
+                    long initialTime = System_Delegate.nanoTime();
+                    if (!mFirstFrameExecuted) {
+                        // The first frame will initialize the animations
+                        Choreographer_Delegate.doFrame(initialTime);
+                        mFirstFrameExecuted = true;
+                    }
+                    // Second frame will move the animations
+                    Choreographer_Delegate.doFrame(initialTime + mElapsedFrameTimeNanos);
+                }
                 mViewRoot.draw(mCanvas);
             }
 
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/animated_vector.png b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/animated_vector.png
new file mode 100644
index 0000000..9f26627
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/animated_vector.png
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/animated_vector_1.png b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/animated_vector_1.png
new file mode 100644
index 0000000..89009be
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/animated_vector_1.png
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/indeterminate_progressbar.xml b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/indeterminate_progressbar.xml
new file mode 100644
index 0000000..70d7396
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/indeterminate_progressbar.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:padding="16dp"
+              android:orientation="horizontal"
+              android:layout_width="fill_parent"
+              android:layout_height="fill_parent">
+
+    <ProgressBar
+             android:layout_height="fill_parent"
+             android:layout_width="fill_parent" />
+
+</LinearLayout>
+
diff --git a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
index 9ebeebd..2dca07c 100644
--- a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
+++ b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
@@ -48,6 +48,8 @@
 import java.net.URL;
 import java.util.Arrays;
 import java.util.Comparator;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
 
 import static org.junit.Assert.fail;
 
@@ -348,16 +350,46 @@
         renderAndVerify(params, "expand_horz_layout.png");
     }
 
+    /** Test expand_layout.xml */
+    @Test
+    public void testVectorAnimation() throws ClassNotFoundException {
+        // Create the layout pull parser.
+        LayoutPullParser parser = new LayoutPullParser(APP_TEST_RES + "/layout/" +
+                "indeterminate_progressbar.xml");
+        // Create LayoutLibCallback.
+        LayoutLibTestCallback layoutLibCallback = new LayoutLibTestCallback(getLogger());
+        layoutLibCallback.initResources();
+
+        SessionParams params = getSessionParams(parser, ConfigGenerator.NEXUS_5,
+                layoutLibCallback, "Theme.Material.NoActionBar.Fullscreen", false,
+                RenderingMode.V_SCROLL, 22);
+
+        renderAndVerify(params, "animated_vector.png", TimeUnit.SECONDS.toNanos(2));
+
+        parser = new LayoutPullParser(APP_TEST_RES + "/layout/" +
+                "indeterminate_progressbar.xml");
+        params = getSessionParams(parser, ConfigGenerator.NEXUS_5,
+                layoutLibCallback, "Theme.Material.NoActionBar.Fullscreen", false,
+                RenderingMode.V_SCROLL, 22);
+        renderAndVerify(params, "animated_vector_1.png", TimeUnit.SECONDS.toNanos(3));
+    }
+
     /**
      * Create a new rendering session and test that rendering given layout on nexus 5
      * doesn't throw any exceptions and matches the provided image.
+     * <p/>If frameTimeNanos is >= 0 a frame will be executed during the rendering. The time
+     * indicates how far in the future is.
      */
-    private void renderAndVerify(SessionParams params, String goldenFileName)
+    private void renderAndVerify(SessionParams params, String goldenFileName, long frameTimeNanos)
             throws ClassNotFoundException {
         // TODO: Set up action bar handler properly to test menu rendering.
         // Create session params.
         RenderSession session = sBridge.createSession(params);
 
+        if (frameTimeNanos != -1) {
+            session.setElapsedFrameTimeNanos(frameTimeNanos);
+        }
+
         if (!session.getResult().isSuccess()) {
             getLogger().error(session.getResult().getException(),
                     session.getResult().getErrorMessage());
@@ -380,6 +412,15 @@
      * Create a new rendering session and test that rendering given layout on nexus 5
      * doesn't throw any exceptions and matches the provided image.
      */
+    private void renderAndVerify(SessionParams params, String goldenFileName)
+            throws ClassNotFoundException {
+        renderAndVerify(params, goldenFileName, -1);
+    }
+
+    /**
+     * Create a new rendering session and test that rendering given layout on nexus 5
+     * doesn't throw any exceptions and matches the provided image.
+     */
     private void renderAndVerify(String layoutFileName, String goldenFileName)
             throws ClassNotFoundException {
         // Create the layout pull parser.
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java
index f6c2626..8f0ad01 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java
@@ -77,6 +77,8 @@
     /** Methods to inject. FQCN of class in which method should be injected => runnable that does
      * the injection. */
     private final Map<String, ICreateInfo.InjectMethodRunnable> mInjectedMethodsMap;
+    /** A map { FQCN => set { field names } } which should be promoted to public visibility */
+    private final Map<String, Set<String>> mPromotedFields;
 
     /**
      * Creates a new generator that can generate the output JAR with the stubbed classes.
@@ -109,20 +111,8 @@
 
         // Create the map/set of methods to change to delegates
         mDelegateMethods = new HashMap<String, Set<String>>();
-        for (String signature : createInfo.getDelegateMethods()) {
-            int pos = signature.indexOf('#');
-            if (pos <= 0 || pos >= signature.length() - 1) {
-                continue;
-            }
-            String className = binaryToInternalClassName(signature.substring(0, pos));
-            String methodName = signature.substring(pos + 1);
-            Set<String> methods = mDelegateMethods.get(className);
-            if (methods == null) {
-                methods = new HashSet<String>();
-                mDelegateMethods.put(className, methods);
-            }
-            methods.add(methodName);
-        }
+        addToMap(createInfo.getDelegateMethods(), mDelegateMethods);
+
         for (String className : createInfo.getDelegateClassNatives()) {
             className = binaryToInternalClassName(className);
             Set<String> methods = mDelegateMethods.get(className);
@@ -187,10 +177,34 @@
             returnTypes.add(binaryToInternalClassName(className));
         }
 
+        mPromotedFields = new HashMap<String, Set<String>>();
+        addToMap(createInfo.getPromotedFields(), mPromotedFields);
+
         mInjectedMethodsMap = createInfo.getInjectedMethodsMap();
     }
 
     /**
+     * For each value in the array, split the value on '#' and add the parts to the map as key
+     * and value.
+     */
+    private void addToMap(String[] entries, Map<String, Set<String>> map) {
+        for (String entry : entries) {
+            int pos = entry.indexOf('#');
+            if (pos <= 0 || pos >= entry.length() - 1) {
+                return;
+            }
+            String className = binaryToInternalClassName(entry.substring(0, pos));
+            String methodOrFieldName = entry.substring(pos + 1);
+            Set<String> set = map.get(className);
+            if (set == null) {
+                set = new HashSet<String>();
+                map.put(className, set);
+            }
+            set.add(methodOrFieldName);
+        }
+    }
+
+    /**
      * Returns the list of classes that have not been renamed yet.
      * <p/>
      * The names are "internal class names" rather than FQCN, i.e. they use "/" instead "."
@@ -380,6 +394,10 @@
             }
         }
 
+        Set<String> promoteFields = mPromotedFields.get(className);
+        if (promoteFields != null && !promoteFields.isEmpty()) {
+            cv = new PromoteFieldClassAdapter(cv, promoteFields);
+        }
         cr.accept(cv, 0);
         return cw.toByteArray();
     }
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
index e480ead..b571c5a 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
@@ -120,6 +120,11 @@
     }
 
     @Override
+    public String[] getPromotedFields() {
+        return PROMOTED_FIELDS;
+    }
+
+    @Override
     public Map<String, InjectMethodRunnable> getInjectedMethodsMap() {
         return INJECTED_METHODS;
     }
@@ -169,7 +174,9 @@
         "android.text.format.DateFormat#is24HourFormat",
         "android.text.Hyphenator#getSystemHyphenatorLocation",
         "android.util.Xml#newPullParser",
+        "android.view.Choreographer#getInstance",
         "android.view.Choreographer#getRefreshRate",
+        "android.view.Choreographer#scheduleVsyncLocked",
         "android.view.Display#updateDisplayInfoLocked",
         "android.view.Display#getWindowManager",
         "android.view.LayoutInflater#rInflate",
@@ -290,6 +297,10 @@
             "org.kxml2.io.KXmlParser"
         };
 
+    private final static String[] PROMOTED_FIELDS = new String[] {
+        "android.view.Choreographer#mLastFrameTimeNanos"
+    };
+
     /**
      * List of classes for which the methods returning them should be deleted.
      * The array contains a list of null terminated section starting with the name of the class
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ICreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ICreateInfo.java
index 54b1fe6..6c62423 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ICreateInfo.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ICreateInfo.java
@@ -78,6 +78,13 @@
     Set<String> getExcludedClasses();
 
     /**
+     * Returns a list of fields which should be promoted to public visibility. The array values
+     * are in the form of the binary FQCN of the class containing the field and the field name
+     * separated by a '#'.
+     */
+    String[] getPromotedFields();
+
+    /**
      * Returns a map from binary FQCN className to {@link InjectMethodRunnable} which will be
      * called to inject methods into a class.
      * Can be empty but must not be null.
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/PromoteFieldClassAdapter.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/PromoteFieldClassAdapter.java
new file mode 100644
index 0000000..e4b70da
--- /dev/null
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/PromoteFieldClassAdapter.java
@@ -0,0 +1,52 @@
+/*
+ * 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.tools.layoutlib.create;
+
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.FieldVisitor;
+
+import java.util.Set;
+
+import static org.objectweb.asm.Opcodes.ACC_PRIVATE;
+import static org.objectweb.asm.Opcodes.ACC_PROTECTED;
+import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
+import static org.objectweb.asm.Opcodes.ASM4;
+
+/**
+ * Promotes given fields to public visibility.
+ */
+public class PromoteFieldClassAdapter extends ClassVisitor {
+
+    private final Set<String> mFieldNames;
+    private static final int ACC_NOT_PUBLIC = ~(ACC_PRIVATE | ACC_PROTECTED);
+
+    public PromoteFieldClassAdapter(ClassVisitor cv, Set<String> fieldNames) {
+        super(ASM4, cv);
+        mFieldNames = fieldNames;
+    }
+
+    @Override
+    public FieldVisitor visitField(int access, String name, String desc, String signature,
+            Object value) {
+        if (mFieldNames.contains(name)) {
+            if ((access & ACC_PUBLIC) == 0) {
+                access = (access & ACC_NOT_PUBLIC) | ACC_PUBLIC;
+            }
+        }
+        return super.visitField(access, name, desc, signature, value);
+    }
+}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ReplaceMethodCallsAdapter.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ReplaceMethodCallsAdapter.java
index 0b85c48..5e47261 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ReplaceMethodCallsAdapter.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ReplaceMethodCallsAdapter.java
@@ -134,7 +134,33 @@
             }
         });
 
-        // Case 5: java.util.LinkedHashMap.eldest()
+        // Case 5: java.lang.System time calls
+        METHOD_REPLACERS.add(new MethodReplacer() {
+            @Override
+            public boolean isNeeded(String owner, String name, String desc, String sourceClass) {
+                return JAVA_LANG_SYSTEM.equals(owner) && name.equals("nanoTime");
+            }
+
+            @Override
+            public void replace(MethodInformation mi) {
+                mi.name = "nanoTime";
+                mi.owner = Type.getInternalName(System_Delegate.class);
+            }
+        });
+        METHOD_REPLACERS.add(new MethodReplacer() {
+            @Override
+            public boolean isNeeded(String owner, String name, String desc, String sourceClass) {
+                return JAVA_LANG_SYSTEM.equals(owner) && name.equals("currentTimeMillis");
+            }
+
+            @Override
+            public void replace(MethodInformation mi) {
+                mi.name = "currentTimeMillis";
+                mi.owner = Type.getInternalName(System_Delegate.class);
+            }
+        });
+
+        // Case 6: java.util.LinkedHashMap.eldest()
         METHOD_REPLACERS.add(new MethodReplacer() {
 
             private final String VOID_TO_MAP_ENTRY =
@@ -157,7 +183,7 @@
             }
         });
 
-        // Case 6: android.content.Context.getClassLoader() in LayoutInflater
+        // Case 7: android.content.Context.getClassLoader() in LayoutInflater
         METHOD_REPLACERS.add(new MethodReplacer() {
             // When LayoutInflater asks for a class loader, we must return the class loader that
             // cannot return app's custom views/classes. This is so that in case of any failure
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/java/System_Delegate.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/java/System_Delegate.java
index 613c8d9..be93744 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/java/System_Delegate.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/java/System_Delegate.java
@@ -18,12 +18,22 @@
 
 import com.android.tools.layoutlib.create.ReplaceMethodCallsAdapter;
 
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+
 /**
  * Provides dummy implementation of methods that don't exist on the host VM.
+ * This also providers a time control that allows to set a specific system time.
  *
  * @see ReplaceMethodCallsAdapter
  */
+@SuppressWarnings("unused")
 public class System_Delegate {
+    // Current system time
+    private static AtomicLong mNanosTime = new AtomicLong(System.nanoTime());
+    // Time that the system booted up in nanos
+    private static AtomicLong mBootNanosTime = new AtomicLong(System.nanoTime());
+
     public static void log(String message) {
         // ignore.
     }
@@ -31,4 +41,28 @@
     public static void log(String message, Throwable th) {
         // ignore.
     }
+
+    public static void setNanosTime(long nanos) {
+        mNanosTime.set(nanos);
+    }
+
+    public static void setBootTimeNanos(long nanos) {
+        mBootNanosTime.set(nanos);
+    }
+
+    public static long nanoTime() {
+        return mNanosTime.get();
+    }
+
+    public static long currentTimeMillis() {
+        return TimeUnit.NANOSECONDS.toMillis(mNanosTime.get());
+    }
+
+    public static long bootTime() {
+        return mBootNanosTime.get();
+    }
+
+    public static long bootTimeMillis() {
+        return TimeUnit.NANOSECONDS.toMillis(mBootNanosTime.get());
+    }
 }
diff --git a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmGeneratorTest.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmGeneratorTest.java
index 2c21470..8a2235b 100644
--- a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmGeneratorTest.java
+++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmGeneratorTest.java
@@ -138,6 +138,11 @@
             }
 
             @Override
+            public String[] getPromotedFields() {
+                return new String[0];
+            }
+
+            @Override
             public Map<String, InjectMethodRunnable> getInjectedMethodsMap() {
                 return new HashMap<String, InjectMethodRunnable>(0);
             }
@@ -213,6 +218,11 @@
             }
 
             @Override
+            public String[] getPromotedFields() {
+                return new String[0];
+            }
+
+            @Override
             public Map<String, InjectMethodRunnable> getInjectedMethodsMap() {
                 return new HashMap<String, InjectMethodRunnable>(0);
             }
@@ -296,6 +306,11 @@
             }
 
             @Override
+            public String[] getPromotedFields() {
+                return new String[0];
+            }
+
+            @Override
             public Map<String, InjectMethodRunnable> getInjectedMethodsMap() {
                 return new HashMap<String, InjectMethodRunnable>(0);
             }
@@ -374,6 +389,11 @@
             }
 
             @Override
+            public String[] getPromotedFields() {
+                return new String[0];
+            }
+
+            @Override
             public Map<String, InjectMethodRunnable> getInjectedMethodsMap() {
                 HashMap<String, InjectMethodRunnable> map =
                         new HashMap<String, InjectMethodRunnable>(1);
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index bd030e8..b054f7c 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -665,7 +665,7 @@
      * </ul>
      * @return a list of network configurations in the form of a list
      * of {@link WifiConfiguration} objects. Upon failure to fetch or
-     * when when Wi-Fi is turned off, it can be null.
+     * when Wi-Fi is turned off, it can be null.
      */
     public List<WifiConfiguration> getConfiguredNetworks() {
         try {