Merge "Fix reporting of uid state in battery stats."
diff --git a/api/current.txt b/api/current.txt
index fc95ca3..7ddd5af 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -7809,6 +7809,7 @@
     method public abstract java.lang.String getPackageResourcePath();
     method public abstract android.content.res.Resources getResources();
     method public abstract android.content.SharedPreferences getSharedPreferences(java.lang.String, int);
+    method public abstract android.content.SharedPreferences getSharedPreferences(java.io.File, int);
     method public final java.lang.String getString(int);
     method public final java.lang.String getString(int, java.lang.Object...);
     method public abstract java.lang.Object getSystemService(java.lang.String);
@@ -7991,6 +7992,7 @@
     method public java.lang.String getPackageResourcePath();
     method public android.content.res.Resources getResources();
     method public android.content.SharedPreferences getSharedPreferences(java.lang.String, int);
+    method public android.content.SharedPreferences getSharedPreferences(java.io.File, int);
     method public java.lang.Object getSystemService(java.lang.String);
     method public java.lang.String getSystemServiceName(java.lang.Class<?>);
     method public android.content.res.Resources.Theme getTheme();
@@ -28862,6 +28864,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();
   }
@@ -36201,6 +36205,7 @@
     method public java.lang.String getPackageResourcePath();
     method public android.content.res.Resources getResources();
     method public android.content.SharedPreferences getSharedPreferences(java.lang.String, int);
+    method public android.content.SharedPreferences getSharedPreferences(java.io.File, int);
     method public java.lang.Object getSystemService(java.lang.String);
     method public java.lang.String getSystemServiceName(java.lang.Class<?>);
     method public android.content.res.Resources.Theme getTheme();
diff --git a/api/system-current.txt b/api/system-current.txt
index 409a7d5..edc9f18 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -8052,6 +8052,7 @@
     method public abstract java.lang.String getPackageResourcePath();
     method public abstract android.content.res.Resources getResources();
     method public abstract android.content.SharedPreferences getSharedPreferences(java.lang.String, int);
+    method public abstract android.content.SharedPreferences getSharedPreferences(java.io.File, int);
     method public final java.lang.String getString(int);
     method public final java.lang.String getString(int, java.lang.Object...);
     method public abstract java.lang.Object getSystemService(java.lang.String);
@@ -8243,6 +8244,7 @@
     method public java.lang.String getPackageResourcePath();
     method public android.content.res.Resources getResources();
     method public android.content.SharedPreferences getSharedPreferences(java.lang.String, int);
+    method public android.content.SharedPreferences getSharedPreferences(java.io.File, int);
     method public java.lang.Object getSystemService(java.lang.String);
     method public java.lang.String getSystemServiceName(java.lang.Class<?>);
     method public android.content.res.Resources.Theme getTheme();
@@ -30845,6 +30847,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();
   }
@@ -38516,6 +38520,7 @@
     method public java.lang.String getPackageResourcePath();
     method public android.content.res.Resources getResources();
     method public android.content.SharedPreferences getSharedPreferences(java.lang.String, int);
+    method public android.content.SharedPreferences getSharedPreferences(java.io.File, int);
     method public java.lang.Object getSystemService(java.lang.String);
     method public java.lang.String getSystemServiceName(java.lang.Class<?>);
     method public android.content.res.Resources.Theme getTheme();
diff --git a/api/test-current.txt b/api/test-current.txt
index fc95ca3..4838702 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -7809,6 +7809,7 @@
     method public abstract java.lang.String getPackageResourcePath();
     method public abstract android.content.res.Resources getResources();
     method public abstract android.content.SharedPreferences getSharedPreferences(java.lang.String, int);
+    method public abstract android.content.SharedPreferences getSharedPreferences(java.io.File, int);
     method public final java.lang.String getString(int);
     method public final java.lang.String getString(int, java.lang.Object...);
     method public abstract java.lang.Object getSystemService(java.lang.String);
@@ -7991,6 +7992,7 @@
     method public java.lang.String getPackageResourcePath();
     method public android.content.res.Resources getResources();
     method public android.content.SharedPreferences getSharedPreferences(java.lang.String, int);
+    method public android.content.SharedPreferences getSharedPreferences(java.io.File, int);
     method public java.lang.Object getSystemService(java.lang.String);
     method public java.lang.String getSystemServiceName(java.lang.Class<?>);
     method public android.content.res.Resources.Theme getTheme();
@@ -28743,7 +28745,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
@@ -28862,6 +28866,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();
   }
@@ -36201,6 +36207,7 @@
     method public java.lang.String getPackageResourcePath();
     method public android.content.res.Resources getResources();
     method public android.content.SharedPreferences getSharedPreferences(java.lang.String, int);
+    method public android.content.SharedPreferences getSharedPreferences(java.io.File, int);
     method public java.lang.Object getSystemService(java.lang.String);
     method public java.lang.String getSystemServiceName(java.lang.Class<?>);
     method public android.content.res.Resources.Theme getTheme();
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/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index f1a7de8..a847a4d 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;
+        }
     }
 
     /**
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/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 bc7c3d0..c661107 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -129,7 +129,7 @@
     /**
      * Map from package name, to preference name, to cached preferences.
      */
-    private static ArrayMap<String, ArrayMap<String, SharedPreferencesImpl>> sSharedPrefs;
+    private static ArrayMap<String, ArrayMap<File, SharedPreferencesImpl>> sSharedPrefs;
 
     final ActivityThread mMainThread;
     final LoadedApk mPackageInfo;
@@ -327,34 +327,39 @@
 
     @Override
     public SharedPreferences getSharedPreferences(String name, int mode) {
+        // At least one application in the world actually passes in a null
+        // name.  This happened to work because when we generated the file name
+        // we would stringify it to "null.xml".  Nice.
+        if (mPackageInfo.getApplicationInfo().targetSdkVersion <
+                Build.VERSION_CODES.KITKAT) {
+            if (name == null) {
+                name = "null";
+            }
+        }
+
+        final File file = getSharedPrefsFile(name);
+        return getSharedPreferences(file, mode);
+    }
+
+    @Override
+    public SharedPreferences getSharedPreferences(File file, int mode) {
         SharedPreferencesImpl sp;
         synchronized (ContextImpl.class) {
             if (sSharedPrefs == null) {
-                sSharedPrefs = new ArrayMap<String, ArrayMap<String, SharedPreferencesImpl>>();
+                sSharedPrefs = new ArrayMap<String, ArrayMap<File, SharedPreferencesImpl>>();
             }
 
             final String packageName = getPackageName();
-            ArrayMap<String, SharedPreferencesImpl> packagePrefs = sSharedPrefs.get(packageName);
+            ArrayMap<File, SharedPreferencesImpl> packagePrefs = sSharedPrefs.get(packageName);
             if (packagePrefs == null) {
-                packagePrefs = new ArrayMap<String, SharedPreferencesImpl>();
+                packagePrefs = new ArrayMap<File, SharedPreferencesImpl>();
                 sSharedPrefs.put(packageName, packagePrefs);
             }
 
-            // At least one application in the world actually passes in a null
-            // name.  This happened to work because when we generated the file name
-            // we would stringify it to "null.xml".  Nice.
-            if (mPackageInfo.getApplicationInfo().targetSdkVersion <
-                    Build.VERSION_CODES.KITKAT) {
-                if (name == null) {
-                    name = "null";
-                }
-            }
-
-            sp = packagePrefs.get(name);
+            sp = packagePrefs.get(file);
             if (sp == null) {
-                File prefsFile = getSharedPrefsFile(name);
-                sp = new SharedPreferencesImpl(prefsFile, mode);
-                packagePrefs.put(name, sp);
+                sp = new SharedPreferencesImpl(file, mode);
+                packagePrefs.put(file, sp);
                 return sp;
             }
         }
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/Notification.java b/core/java/android/app/Notification.java
index 74634a9..6c0c3e8 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -25,6 +25,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.res.ColorStateList;
 import android.graphics.Bitmap;
@@ -45,7 +46,6 @@
 import android.os.UserHandle;
 import android.text.TextUtils;
 import android.util.Log;
-import android.util.MathUtils;
 import android.util.TypedValue;
 import android.view.Gravity;
 import android.view.View;
@@ -58,7 +58,6 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.reflect.Constructor;
-import java.text.NumberFormat;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -2969,7 +2968,6 @@
             Bitmap profileBadge = getProfileBadge();
 
             contentView.setViewVisibility(R.id.profile_badge_large_template, View.GONE);
-            contentView.setViewVisibility(R.id.profile_badge_line2, View.GONE);
             contentView.setViewVisibility(R.id.profile_badge_line3, View.GONE);
 
             if (profileBadge != null) {
@@ -2986,38 +2984,35 @@
             return false;
         }
 
-        private void shrinkLine3Text(RemoteViews contentView) {
-            float subTextSize = mContext.getResources().getDimensionPixelSize(
-                    R.dimen.notification_subtext_size);
-            contentView.setTextViewTextSize(R.id.text, TypedValue.COMPLEX_UNIT_PX, subTextSize);
-        }
-
-        private void unshrinkLine3Text(RemoteViews contentView) {
-            float regularTextSize = mContext.getResources().getDimensionPixelSize(
-                    com.android.internal.R.dimen.notification_text_size);
-            contentView.setTextViewTextSize(R.id.text, TypedValue.COMPLEX_UNIT_PX, regularTextSize);
-        }
-
         private void resetStandardTemplate(RemoteViews contentView) {
-            removeLargeIconBackground(contentView);
-            contentView.setViewPadding(R.id.icon, 0, 0, 0, 0);
-            contentView.setImageViewResource(R.id.icon, 0);
-            contentView.setInt(R.id.icon, "setBackgroundResource", 0);
+            resetNotificationHeader(contentView);
+            resetContentMargins(contentView);
             contentView.setViewVisibility(R.id.right_icon, View.GONE);
-            contentView.setInt(R.id.right_icon, "setBackgroundResource", 0);
-            contentView.setImageViewResource(R.id.right_icon, 0);
-            contentView.setImageViewResource(R.id.icon, 0);
             contentView.setTextViewText(R.id.title, null);
             contentView.setTextViewText(R.id.text, null);
-            unshrinkLine3Text(contentView);
-            contentView.setTextViewText(R.id.text2, null);
-            contentView.setViewVisibility(R.id.text2, View.GONE);
-            contentView.setViewVisibility(R.id.info, View.GONE);
-            contentView.setViewVisibility(R.id.time, View.GONE);
             contentView.setViewVisibility(R.id.line3, View.GONE);
-            contentView.setViewVisibility(R.id.overflow_divider, View.GONE);
+            contentView.setViewVisibility(R.id.text_line_1, View.GONE);
             contentView.setViewVisibility(R.id.progress, View.GONE);
+        }
+
+        /**
+         * Resets the notification header to its original state
+         */
+        private void resetNotificationHeader(RemoteViews contentView) {
+            contentView.setImageViewResource(R.id.icon, 0);
+            contentView.setTextViewText(R.id.app_name_text, null);
             contentView.setViewVisibility(R.id.chronometer, View.GONE);
+            contentView.setViewVisibility(R.id.header_sub_text, View.GONE);
+            contentView.setViewVisibility(R.id.header_content_info, View.GONE);
+            contentView.setViewVisibility(R.id.number_of_children, View.GONE);
+            contentView.setViewVisibility(R.id.sub_text_divider, View.GONE);
+            contentView.setViewVisibility(R.id.content_info_divider, View.GONE);
+            contentView.setViewVisibility(R.id.time_divider, View.GONE);
+        }
+
+        private void resetContentMargins(RemoteViews contentView) {
+            contentView.setViewLayoutMarginEnd(R.id.line1, 0);
+            contentView.setViewLayoutMarginEnd(R.id.line3, 0);
         }
 
         private RemoteViews applyStandardTemplate(int resId) {
@@ -3033,95 +3028,118 @@
             resetStandardTemplate(contentView);
 
             boolean showLine3 = false;
-            boolean showLine2 = false;
-            boolean contentTextInLine2 = false;
             final Bundle ex = mN.extras;
 
-            if (mN.mLargeIcon != null) {
-                contentView.setImageViewIcon(R.id.icon, mN.mLargeIcon);
-                processLargeLegacyIcon(mN.mLargeIcon, contentView);
-                contentView.setImageViewIcon(R.id.right_icon, mN.mSmallIcon);
-                contentView.setViewVisibility(R.id.right_icon, View.VISIBLE);
-                processSmallRightIcon(mN.mSmallIcon, contentView);
-            } else { // small icon at left
-                contentView.setImageViewIcon(R.id.icon, mN.mSmallIcon);
-                contentView.setViewVisibility(R.id.icon, View.VISIBLE);
-                processSmallIconAsLarge(mN.mSmallIcon, contentView);
-            }
+            bindNotificationHeader(contentView);
+            bindLargeIcon(contentView);
             if (ex.getCharSequence(EXTRA_TITLE) != null) {
                 contentView.setTextViewText(R.id.title,
                         processLegacyText(ex.getCharSequence(EXTRA_TITLE)));
             }
+            boolean showProgress = handleProgressBar(hasProgress, contentView, ex);
             if (ex.getCharSequence(EXTRA_TEXT) != null) {
-                contentView.setTextViewText(R.id.text,
+                contentView.setTextViewText(showProgress ? R.id.text_line_1 : R.id.text,
                         processLegacyText(ex.getCharSequence(EXTRA_TEXT)));
+                if (showProgress) {
+                    contentView.setViewVisibility(R.id.text_line_1, View.VISIBLE);
+                }
+                showLine3 = !showProgress;
+            }
+            // We want to add badge to first line of text.
+            if (addProfileBadge(contentView, R.id.profile_badge_line3)) {
                 showLine3 = true;
             }
-            if (ex.getCharSequence(EXTRA_INFO_TEXT) != null) {
-                contentView.setTextViewText(R.id.info,
-                        processLegacyText(ex.getCharSequence(EXTRA_INFO_TEXT)));
-                contentView.setViewVisibility(R.id.info, View.VISIBLE);
-                showLine3 = true;
+            // Note getStandardView may hide line 3 again.
+            contentView.setViewVisibility(R.id.line3, showLine3 ? View.VISIBLE : View.GONE);
+
+            return contentView;
+        }
+
+        private boolean handleProgressBar(boolean hasProgress, RemoteViews contentView, Bundle ex) {
+            final int max = ex.getInt(EXTRA_PROGRESS_MAX, 0);
+            final int progress = ex.getInt(EXTRA_PROGRESS, 0);
+            final boolean ind = ex.getBoolean(EXTRA_PROGRESS_INDETERMINATE);
+            if (hasProgress && (max != 0 || ind)) {
+                contentView.setViewVisibility(com.android.internal.R.id.progress, View.VISIBLE);
+                contentView.setProgressBar(
+                        R.id.progress, max, progress, ind);
+                contentView.setProgressBackgroundTintList(
+                        R.id.progress, ColorStateList.valueOf(mContext.getColor(
+                                R.color.notification_progress_background_color)));
+                if (mN.color != COLOR_DEFAULT) {
+                    ColorStateList colorStateList = ColorStateList.valueOf(mN.color);
+                    contentView.setProgressTintList(R.id.progress, colorStateList);
+                    contentView.setProgressIndeterminateTintList(R.id.progress, colorStateList);
+                }
+                return true;
+            } else {
+                contentView.setViewVisibility(R.id.progress, View.GONE);
+                return false;
+            }
+        }
+
+        private void bindLargeIcon(RemoteViews contentView) {
+            if (mN.mLargeIcon != null) {
+                contentView.setViewVisibility(R.id.right_icon, View.VISIBLE);
+                contentView.setImageViewIcon(R.id.right_icon, mN.mLargeIcon);
+                processLargeLegacyIcon(mN.mLargeIcon, contentView);
+                int endMargin = mContext.getResources().getDimensionPixelSize(
+                        R.dimen.notification_content_picture_margin);
+                contentView.setViewLayoutMarginEnd(R.id.line1, endMargin);
+                contentView.setViewLayoutMarginEnd(R.id.line3, endMargin);
+                contentView.setViewLayoutMarginEnd(R.id.progress, endMargin);
+            }
+        }
+
+        private void bindNotificationHeader(RemoteViews contentView) {
+            bindSmallIcon(contentView);
+            bindChildCountColor(contentView);
+            bindHeaderAppName(contentView);
+            bindHeaderSubText(contentView);
+            bindContentInfo(contentView);
+            bindHeaderChronometerAndTime(contentView);
+            bindExpandButton(contentView);
+        }
+
+        private void bindChildCountColor(RemoteViews contentView) {
+            contentView.setTextColor(R.id.number_of_children, resolveColor());
+        }
+
+        private void bindContentInfo(RemoteViews contentView) {
+            boolean visible = false;
+            if (mN.extras.getCharSequence(EXTRA_INFO_TEXT) != null) {
+                contentView.setTextViewText(R.id.header_content_info,
+                        processLegacyText(mN.extras.getCharSequence(EXTRA_INFO_TEXT)));
+                contentView.setViewVisibility(R.id.header_content_info, View.VISIBLE);
+                visible = true;
             } else if (mN.number > 0) {
                 final int tooBig = mContext.getResources().getInteger(
                         R.integer.status_bar_notification_info_maxnum);
                 if (mN.number > tooBig) {
-                    contentView.setTextViewText(R.id.info, processLegacyText(
+                    contentView.setTextViewText(R.id.header_content_info, processLegacyText(
                             mContext.getResources().getString(
                                     R.string.status_bar_notification_info_overflow)));
                 } else {
-                    NumberFormat f = NumberFormat.getIntegerInstance();
-                    contentView.setTextViewText(R.id.info, processLegacyText(f.format(mN.number)));
+                    contentView.setTextViewText(R.id.header_content_info,
+                            processLegacyText(String.valueOf(mN.number)));
                 }
-                contentView.setViewVisibility(R.id.info, View.VISIBLE);
-                showLine3 = true;
-            } else {
-                contentView.setViewVisibility(R.id.info, View.GONE);
+                contentView.setViewVisibility(R.id.header_content_info, View.VISIBLE);
+                visible = true;
             }
-
-            // Need to show three lines?
-            if (ex.getCharSequence(EXTRA_SUB_TEXT) != null) {
-                contentView.setTextViewText(R.id.text,
-                        processLegacyText(ex.getCharSequence(EXTRA_SUB_TEXT)));
-                if (ex.getCharSequence(EXTRA_TEXT) != null) {
-                    contentView.setTextViewText(R.id.text2,
-                            processLegacyText(ex.getCharSequence(EXTRA_TEXT)));
-                    contentView.setViewVisibility(R.id.text2, View.VISIBLE);
-                    showLine2 = true;
-                    contentTextInLine2 = true;
-                } else {
-                    contentView.setViewVisibility(R.id.text2, View.GONE);
-                }
-            } else {
-                contentView.setViewVisibility(R.id.text2, View.GONE);
-                final int max = ex.getInt(EXTRA_PROGRESS_MAX, 0);
-                final int progress = ex.getInt(EXTRA_PROGRESS, 0);
-                final boolean ind = ex.getBoolean(EXTRA_PROGRESS_INDETERMINATE);
-                if (hasProgress && (max != 0 || ind)) {
-                    contentView.setViewVisibility(R.id.progress, View.VISIBLE);
-                    contentView.setProgressBar(
-                            R.id.progress, max, progress, ind);
-                    contentView.setProgressBackgroundTintList(
-                            R.id.progress, ColorStateList.valueOf(mContext.getColor(
-                                    R.color.notification_progress_background_color)));
-                    if (mN.color != COLOR_DEFAULT) {
-                        ColorStateList colorStateList = ColorStateList.valueOf(mN.color);
-                        contentView.setProgressTintList(R.id.progress, colorStateList);
-                        contentView.setProgressIndeterminateTintList(R.id.progress, colorStateList);
-                    }
-                    showLine2 = true;
-                } else {
-                    contentView.setViewVisibility(R.id.progress, View.GONE);
-                }
+            if (visible) {
+                contentView.setViewVisibility(R.id.content_info_divider, View.VISIBLE);
             }
-            if (showLine2) {
+        }
 
-                // need to shrink all the type to make sure everything fits
-                shrinkLine3Text(contentView);
-            }
+        private void bindExpandButton(RemoteViews contentView) {
+            contentView.setDrawableParameters(R.id.expand_button, false, -1, resolveColor(),
+                    PorterDuff.Mode.SRC_ATOP, -1);
+        }
 
+        private void bindHeaderChronometerAndTime(RemoteViews contentView) {
             if (showsTimeOrChronometer()) {
-                if (ex.getBoolean(EXTRA_SHOW_CHRONOMETER)) {
+                contentView.setViewVisibility(R.id.time_divider, View.VISIBLE);
+                if (mN.extras.getBoolean(EXTRA_SHOW_CHRONOMETER)) {
                     contentView.setViewVisibility(R.id.chronometer, View.VISIBLE);
                     contentView.setLong(R.id.chronometer, "setBase",
                             mN.when + (SystemClock.elapsedRealtime() - System.currentTimeMillis()));
@@ -3131,26 +3149,42 @@
                     contentView.setLong(R.id.time, "setTime", mN.when);
                 }
             }
+        }
 
-            // Adjust padding depending on line count and font size.
-            contentView.setViewPadding(R.id.line1, 0,
-                    calculateTopPadding(mContext, hasThreeLines(),
-                            mContext.getResources().getConfiguration().fontScale),
-                    0, 0);
-
-            // We want to add badge to first line of text.
-            boolean addedBadge = addProfileBadge(contentView,
-                    contentTextInLine2 ? R.id.profile_badge_line2 : R.id.profile_badge_line3);
-            // If we added the badge to line 3 then we should show line 3.
-            if (addedBadge && !contentTextInLine2) {
-                showLine3 = true;
+        private void bindHeaderSubText(RemoteViews contentView) {
+            CharSequence subText = mN.extras.getCharSequence(EXTRA_SUB_TEXT);
+            if (subText == null && mStyle != null && mStyle.mSummaryTextSet
+                    && mStyle.hasSummaryInHeader()) {
+                subText = mStyle.mSummaryText;
             }
+            if (subText != null) {
+                // TODO: Remove the span entirely to only have the string with propper formating.
+                contentView.setTextViewText(R.id.header_sub_text, processLegacyText(subText));
+                contentView.setViewVisibility(R.id.header_sub_text, View.VISIBLE);
+                contentView.setViewVisibility(R.id.sub_text_divider, View.VISIBLE);
+            }
+        }
 
-            // Note getStandardView may hide line 3 again.
-            contentView.setViewVisibility(R.id.line3, showLine3 ? View.VISIBLE : View.GONE);
-            contentView.setViewVisibility(R.id.overflow_divider,
-                    showLine3 ? View.VISIBLE : View.GONE);
-            return contentView;
+        private void bindHeaderAppName(RemoteViews contentView) {
+            PackageManager packageManager = mContext.getPackageManager();
+            ApplicationInfo info = null;
+            try {
+                info = packageManager.getApplicationInfo(mContext.getApplicationInfo().packageName,
+                        0);
+            } catch (final NameNotFoundException e) {
+                return;
+            }
+            CharSequence appName = info != null ? packageManager.getApplicationLabel(info)
+                    : null;
+            if (TextUtils.isEmpty(appName)) {
+                return;
+            }
+            contentView.setTextViewText(R.id.app_name_text, appName);
+        }
+
+        private void bindSmallIcon(RemoteViews contentView) {
+            contentView.setImageViewIcon(R.id.icon, mN.mSmallIcon);
+            processSmallIconColor(mN.mSmallIcon, contentView);
         }
 
         /**
@@ -3161,49 +3195,6 @@
             return mN.when != 0 && mN.extras.getBoolean(EXTRA_SHOW_WHEN);
         }
 
-        /**
-         * Logic to find out whether the notification is going to have three lines in the contracted
-         * layout. This is used to adjust the top padding.
-         *
-         * @return true if the notification is going to have three lines; false if the notification
-         *         is going to have one or two lines
-         */
-        private boolean hasThreeLines() {
-            final CharSequence subText = mN.extras.getCharSequence(EXTRA_SUB_TEXT);
-            final CharSequence text = mN.extras.getCharSequence(EXTRA_TEXT);
-            boolean contentTextInLine2 = subText != null && text != null;
-            boolean hasProgress = mStyle == null || mStyle.hasProgress();
-            // If we have content text in line 2, badge goes into line 2, or line 3 otherwise
-            boolean badgeInLine3 = getProfileBadgeDrawable() != null && !contentTextInLine2;
-            boolean hasLine3 = text != null || mN.extras.getCharSequence(EXTRA_INFO_TEXT) != null
-                    || mN.number > 0 || badgeInLine3;
-            final Bundle ex = mN.extras;
-            final int max = ex.getInt(EXTRA_PROGRESS_MAX, 0);
-            final boolean ind = ex.getBoolean(EXTRA_PROGRESS_INDETERMINATE);
-            boolean hasLine2 = (subText != null && text != null) ||
-                    (hasProgress && subText == null && (max != 0 || ind));
-            return hasLine2 && hasLine3;
-        }
-
-        /**
-         * @hide
-         */
-        public static int calculateTopPadding(Context ctx, boolean hasThreeLines,
-                float fontScale) {
-            int padding = ctx.getResources().getDimensionPixelSize(hasThreeLines
-                    ? R.dimen.notification_top_pad_narrow
-                    : R.dimen.notification_top_pad);
-            int largePadding = ctx.getResources().getDimensionPixelSize(hasThreeLines
-                    ? R.dimen.notification_top_pad_large_text_narrow
-                    : R.dimen.notification_top_pad_large_text);
-            float largeFactor = (MathUtils.constrain(fontScale, 1.0f, LARGE_TEXT_SCALE) - 1f)
-                    / (LARGE_TEXT_SCALE - 1f);
-
-            // Linearly interpolate the padding between large and normal with the font scale ranging
-            // from 1f to LARGE_TEXT_SCALE
-            return Math.round((1 - largeFactor) * padding + largeFactor * largePadding);
-        }
-
         private void resetStandardTemplateWithActions(RemoteViews big) {
             big.setViewVisibility(R.id.actions, View.GONE);
             big.setViewVisibility(R.id.action_divider, View.GONE);
@@ -3250,18 +3241,46 @@
          * Construct a RemoteViews for the final big notification layout.
          */
         public RemoteViews makeBigContentView() {
+            RemoteViews result = null;
             if (mN.bigContentView != null) {
                 return mN.bigContentView;
             } else if (mStyle != null) {
-                final RemoteViews styleView = mStyle.makeBigContentView();
-                if (styleView != null) {
-                    return styleView;
-                }
+                result = mStyle.makeBigContentView();
             } else if (mActions.size() == 0) {
                 return null;
             }
+            if (result == null) {
+                result = applyStandardTemplateWithActions(getBigBaseLayoutResource());
+            } else {
+                hideLine1Text(result);
+            }
+            adaptNotificationHeaderForBigContentView(result);
+            return result;
+        }
 
-            return applyStandardTemplateWithActions(getBigBaseLayoutResource());
+        /**
+         * Construct a RemoteViews for the final notification header only
+         *
+         * @hide
+         */
+        public RemoteViews makeNotificationHeader() {
+            RemoteViews header = new BuilderRemoteViews(mContext.getApplicationInfo(),
+                    R.layout.notification_template_header);
+            resetNotificationHeader(header);
+            bindNotificationHeader(header);
+            return header;
+        }
+
+        private void hideLine1Text(RemoteViews result) {
+            result.setViewVisibility(R.id.text_line_1, View.GONE);
+        }
+
+        private void adaptNotificationHeaderForBigContentView(RemoteViews result) {
+            // We have to set the collapse button instead
+            result.setImageViewResource(R.id.expand_button, R.drawable.ic_arrow_up_14dp);
+            // Apply the color again
+            result.setDrawableParameters(R.id.expand_button, false, -1, resolveColor(),
+                    PorterDuff.Mode.SRC_ATOP, -1);
         }
 
         /**
@@ -3330,84 +3349,27 @@
         }
 
         /**
-         * Apply any necessary background to smallIcons being used in the largeIcon spot.
+         * Apply any necessariy colors to the small icon
          */
-        private void processSmallIconAsLarge(Icon largeIcon, RemoteViews contentView) {
-            if (!isLegacy()) {
-                contentView.setDrawableParameters(R.id.icon, false, -1,
-                        0xFFFFFFFF,
+        private void processSmallIconColor(Icon smallIcon, RemoteViews contentView) {
+            if (!isLegacy() || getColorUtil().isGrayscaleIcon(mContext, smallIcon)) {
+                contentView.setDrawableParameters(R.id.icon, false, -1, resolveColor(),
                         PorterDuff.Mode.SRC_ATOP, -1);
-                applyLargeIconBackground(contentView);
-            } else {
-                if (getColorUtil().isGrayscaleIcon(mContext, largeIcon)) {
-                    applyLargeIconBackground(contentView);
-                }
             }
         }
 
         /**
-         * Apply any necessary background to a largeIcon if it's a fake smallIcon (that is,
+         * Make the largeIcon dark if it's a fake smallIcon (that is,
          * if it's grayscale).
          */
         // TODO: also check bounds, transparency, that sort of thing.
         private void processLargeLegacyIcon(Icon largeIcon, RemoteViews contentView) {
             if (largeIcon != null && isLegacy()
                     && getColorUtil().isGrayscaleIcon(mContext, largeIcon)) {
-                applyLargeIconBackground(contentView);
-            } else {
-                removeLargeIconBackground(contentView);
-            }
-        }
-
-        /**
-         * Add a colored circle behind the largeIcon slot.
-         */
-        private void applyLargeIconBackground(RemoteViews contentView) {
-            contentView.setInt(R.id.icon, "setBackgroundResource",
-                    R.drawable.notification_icon_legacy_bg);
-
-            contentView.setDrawableParameters(
-                    R.id.icon,
-                    true,
-                    -1,
-                    resolveColor(),
-                    PorterDuff.Mode.SRC_ATOP,
-                    -1);
-
-            int padding = mContext.getResources().getDimensionPixelSize(
-                    R.dimen.notification_large_icon_circle_padding);
-            contentView.setViewPadding(R.id.icon, padding, padding, padding, padding);
-        }
-
-        private void removeLargeIconBackground(RemoteViews contentView) {
-            contentView.setInt(R.id.icon, "setBackgroundResource", 0);
-        }
-
-        /**
-         * Recolor small icons when used in the R.id.right_icon slot.
-         */
-        private void processSmallRightIcon(Icon smallIcon, RemoteViews contentView) {
-            if (!isLegacy()) {
-                contentView.setDrawableParameters(R.id.right_icon, false, -1,
-                        0xFFFFFFFF,
+                // resolve color will fall back to the default when legacy
+                contentView.setDrawableParameters(R.id.icon, false, -1, resolveColor(),
                         PorterDuff.Mode.SRC_ATOP, -1);
             }
-            final boolean gray = isLegacy()
-                    && smallIcon.getType() == Icon.TYPE_RESOURCE
-                    && getColorUtil().isGrayscaleIcon(mContext, smallIcon.getResId());
-            if (!isLegacy() || gray) {
-                contentView.setInt(R.id.right_icon,
-                        "setBackgroundResource",
-                        R.drawable.notification_icon_legacy_bg);
-
-                contentView.setDrawableParameters(
-                        R.id.right_icon,
-                        true,
-                        -1,
-                        resolveColor(),
-                        PorterDuff.Mode.SRC_ATOP,
-                        -1);
-            }
         }
 
         private void sanitizeColor() {
@@ -3416,9 +3378,9 @@
             }
         }
 
-        private int resolveColor() {
+        int resolveColor() {
             if (mN.color == COLOR_DEFAULT) {
-                return mContext.getColor(R.color.notification_icon_bg_color);
+                return mContext.getColor(R.color.notification_icon_default_color);
             }
             return mN.color;
         }
@@ -3622,20 +3584,9 @@
                 contentView.setViewVisibility(R.id.line1, View.VISIBLE);
             }
 
-            // The last line defaults to the subtext, but can be replaced by mSummaryText
-            final CharSequence overflowText =
-                    mSummaryTextSet ? mSummaryText
-                                    : mBuilder.getAllExtras().getCharSequence(EXTRA_SUB_TEXT);
-            if (overflowText != null) {
-                contentView.setTextViewText(R.id.text, mBuilder.processLegacyText(overflowText));
-                contentView.setViewVisibility(R.id.overflow_divider, View.VISIBLE);
-                contentView.setViewVisibility(R.id.line3, View.VISIBLE);
-            } else {
-                // Clear text in case we use the line to show the profile badge.
-                contentView.setTextViewText(R.id.text, "");
-                contentView.setViewVisibility(R.id.overflow_divider, View.GONE);
-                contentView.setViewVisibility(R.id.line3, View.GONE);
-            }
+            // Clear text in case we use the line to show the profile badge.
+            contentView.setTextViewText(com.android.internal.R.id.text, "");
+            contentView.setViewVisibility(com.android.internal.R.id.line3, View.GONE);
 
             return contentView;
         }
@@ -3666,19 +3617,6 @@
         }
 
         /**
-         * Changes the padding of the first line such that the big and small content view have the
-         * same top padding.
-         *
-         * @hide
-         */
-        protected void applyTopPadding(RemoteViews contentView) {
-            int topPadding = Builder.calculateTopPadding(mBuilder.mContext,
-                    mBuilder.hasThreeLines(),
-                    mBuilder.mContext.getResources().getConfiguration().fontScale);
-            contentView.setViewPadding(R.id.line1, 0, topPadding, 0, 0);
-        }
-
-        /**
          * Apply any style-specific extras to this notification before shipping it out.
          * @hide
          */
@@ -3739,6 +3677,14 @@
         protected boolean hasProgress() {
             return true;
         }
+
+        /**
+         * @hide
+         * @return Whether we should put the summary be put into the notification header
+         */
+        public boolean hasSummaryInHeader() {
+            return true;
+        }
     }
 
     /**
@@ -3846,6 +3792,16 @@
             }
 
             RemoteViews contentView = getStandardView(mBuilder.getBigPictureLayoutResource());
+            if (mSummaryTextSet) {
+                contentView.setTextViewText(R.id.text, mBuilder.processLegacyText(mSummaryText));
+                contentView.setViewVisibility(R.id.line3, View.VISIBLE);
+            }
+            int imageMinHeight = mBuilder.mContext.getResources().getDimensionPixelSize(
+                    R.dimen.notification_big_picture_content_min_height_with_picture);
+            // We need to make space for the right image, so we're enforcing a minheight if there
+            // is a picture.
+            int minHeight = (mBuilder.mN.mLargeIcon == null) ? 0 : imageMinHeight;
+            contentView.setInt(R.id.notification_main_column, "setMinimumHeight", minHeight);
 
             if (mBigLargeIconSet) {
                 mBuilder.mN.mLargeIcon = oldLargeIcon;
@@ -3853,12 +3809,7 @@
 
             contentView.setImageViewBitmap(R.id.big_picture, mPicture);
 
-            applyTopPadding(contentView);
-
-            boolean twoTextLines = mBuilder.getAllExtras().getCharSequence(EXTRA_SUB_TEXT) != null
-                    && mBuilder.getAllExtras().getCharSequence(EXTRA_TEXT) != null;
-            mBuilder.addProfileBadge(contentView,
-                    twoTextLines ? R.id.profile_badge_line2 : R.id.profile_badge_line3);
+            mBuilder.addProfileBadge(contentView, R.id.profile_badge_line3);
             return contentView;
         }
 
@@ -3887,6 +3838,14 @@
             }
             mPicture = extras.getParcelable(EXTRA_PICTURE);
         }
+
+        /**
+         * @hide
+         */
+        @Override
+        public boolean hasSummaryInHeader() {
+            return false;
+        }
     }
 
     /**
@@ -3983,14 +3942,11 @@
             contentView.setTextViewText(R.id.big_text, mBuilder.processLegacyText(mBigText));
             contentView.setViewVisibility(R.id.big_text, View.VISIBLE);
             contentView.setInt(R.id.big_text, "setMaxLines", calculateMaxLines());
-            contentView.setViewVisibility(R.id.text2, View.GONE);
-
-            applyTopPadding(contentView);
-
-            mBuilder.shrinkLine3Text(contentView);
 
             mBuilder.addProfileBadge(contentView, R.id.profile_badge_large_template);
 
+            contentView.setBoolean(R.id.big_text, "setHasImage", mBuilder.mN.mLargeIcon != null);
+
             return contentView;
         }
 
@@ -4005,11 +3961,6 @@
             if (hasSummary) {
                 lineCount -= LINES_CONSUMED_BY_SUMMARY;
             }
-
-            // If we have less top padding at the top, we can fit less lines.
-            if (!mBuilder.hasThreeLines()) {
-                lineCount--;
-            }
             return lineCount;
         }
     }
@@ -4105,8 +4056,6 @@
 
             mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, oldBuilderContentText);
 
-            contentView.setViewVisibility(R.id.text2, View.GONE);
-
             int[] rowIds = {R.id.inbox_text0, R.id.inbox_text1, R.id.inbox_text2, R.id.inbox_text3,
                     R.id.inbox_text4, R.id.inbox_text5, R.id.inbox_text6};
 
@@ -4120,6 +4069,9 @@
             final float subTextSize = mBuilder.mContext.getResources().getDimensionPixelSize(
                     R.dimen.notification_subtext_size);
             int i=0;
+            final float density = mBuilder.mContext.getResources().getDisplayMetrics().density;
+            int topPadding = (int) (5 * density);
+            int bottomPadding = (int) (13 * density);
             while (i < mTexts.size() && i < rowIds.length) {
                 CharSequence str = mTexts.get(i);
                 if (str != null && !str.equals("")) {
@@ -4129,24 +4081,29 @@
                         contentView.setTextViewTextSize(rowIds[i], TypedValue.COMPLEX_UNIT_PX,
                                 subTextSize);
                     }
+                    contentView.setViewPadding(rowIds[i], 0, topPadding, 0,
+                            i == rowIds.length - 1 || i == mTexts.size() - 1 ? bottomPadding : 0);
                 }
                 i++;
             }
-
-            contentView.setViewVisibility(R.id.inbox_end_pad,
-                    mTexts.size() > 0 ? View.VISIBLE : View.GONE);
-
-            contentView.setViewVisibility(R.id.inbox_more,
-                    mTexts.size() > rowIds.length ? View.VISIBLE : View.GONE);
-
-            applyTopPadding(contentView);
-
-            mBuilder.shrinkLine3Text(contentView);
-
             mBuilder.addProfileBadge(contentView, R.id.profile_badge_large_template);
 
+            handleInboxImageMargin(contentView, rowIds[0]);
+
             return contentView;
         }
+
+        private void handleInboxImageMargin(RemoteViews contentView, int id) {
+            final int max = mBuilder.mN.extras.getInt(EXTRA_PROGRESS_MAX, 0);
+            final boolean ind = mBuilder.mN.extras.getBoolean(EXTRA_PROGRESS_INDETERMINATE);
+            boolean hasProgress = max != 0 || ind;
+            int endMargin = 0;
+            if (mTexts.size() > 0 && mBuilder.mN.mLargeIcon != null && !hasProgress) {
+                endMargin = mBuilder.mContext.getResources().getDimensionPixelSize(
+                        R.dimen.notification_content_picture_margin);
+            }
+            contentView.setViewLayoutMarginEnd(id, endMargin);
+        }
     }
 
     /**
@@ -4278,14 +4235,13 @@
             }
         }
 
-        private RemoteViews generateMediaActionButton(Action action) {
+        private RemoteViews generateMediaActionButton(Action action, int color) {
             final boolean tombstone = (action.actionIntent == null);
             RemoteViews button = new BuilderRemoteViews(mBuilder.mContext.getApplicationInfo(),
                     R.layout.notification_material_media_action);
             button.setImageViewIcon(R.id.action0, action.getIcon());
-            button.setDrawableParameters(R.id.action0, false, -1,
-                    0xFFFFFFFF,
-                    PorterDuff.Mode.SRC_ATOP, -1);
+            button.setDrawableParameters(R.id.action0, false, -1, color, PorterDuff.Mode.SRC_ATOP,
+                    -1);
             if (!tombstone) {
                 button.setOnClickPendingIntent(R.id.action0, action.actionIntent);
             }
@@ -4311,67 +4267,40 @@
                     }
 
                     final Action action = mBuilder.mActions.get(mActionsToShowInCompact[i]);
-                    final RemoteViews button = generateMediaActionButton(action);
+                    final RemoteViews button = generateMediaActionButton(action,
+                            mBuilder.resolveColor());
                     view.addView(com.android.internal.R.id.media_actions, button);
                 }
             }
-            styleText(view);
-            hideRightIcon(view);
+            handleImage(view  /* addPaddingToMainColumn */);
             return view;
         }
 
         private RemoteViews makeMediaBigContentView() {
             final int actionCount = Math.min(mBuilder.mActions.size(), MAX_MEDIA_BUTTONS);
-            RemoteViews big = mBuilder.applyStandardTemplate(getBigLayoutResource(actionCount),
-                    false /* hasProgress */);
+            RemoteViews big = mBuilder.applyStandardTemplate(
+                    R.layout.notification_template_material_big_media,
+                    false);
 
             if (actionCount > 0) {
                 big.removeAllViews(com.android.internal.R.id.media_actions);
                 for (int i = 0; i < actionCount; i++) {
-                    final RemoteViews button = generateMediaActionButton(mBuilder.mActions.get(i));
+                    final RemoteViews button = generateMediaActionButton(mBuilder.mActions.get(i),
+                            mBuilder.resolveColor());
                     big.addView(com.android.internal.R.id.media_actions, button);
                 }
             }
-            styleText(big);
-            hideRightIcon(big);
-            applyTopPadding(big);
-            big.setViewVisibility(android.R.id.progress, View.GONE);
+            handleImage(big);
             return big;
         }
 
-        private int getBigLayoutResource(int actionCount) {
-            if (actionCount <= 3) {
-                return R.layout.notification_template_material_big_media_narrow;
-            } else {
-                return R.layout.notification_template_material_big_media;
+        private void handleImage(RemoteViews contentView) {
+            if (mBuilder.mN.mLargeIcon != null) {
+                contentView.setViewLayoutMarginEnd(R.id.line1, 0);
+                contentView.setViewLayoutMarginEnd(R.id.line3, 0);
             }
         }
 
-        private void hideRightIcon(RemoteViews contentView) {
-            contentView.setViewVisibility(R.id.right_icon, View.GONE);
-        }
-
-        /**
-         * Applies the special text colors for media notifications to all text views.
-         */
-        private void styleText(RemoteViews contentView) {
-            int primaryColor = mBuilder.mContext.getColor(
-                    R.color.notification_media_primary_color);
-            int secondaryColor = mBuilder.mContext.getColor(
-                    R.color.notification_media_secondary_color);
-            contentView.setTextColor(R.id.title, primaryColor);
-            if (mBuilder.showsTimeOrChronometer()) {
-                if (mBuilder.getAllExtras().getBoolean(EXTRA_SHOW_CHRONOMETER)) {
-                    contentView.setTextColor(R.id.chronometer, secondaryColor);
-                } else {
-                    contentView.setTextColor(R.id.time, secondaryColor);
-                }
-            }
-            contentView.setTextColor(R.id.text2, secondaryColor);
-            contentView.setTextColor(R.id.text, secondaryColor);
-            contentView.setTextColor(R.id.info, secondaryColor);
-        }
-
         /**
          * @hide
          */
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index a0102b6..b73fa50 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -625,8 +625,30 @@
      * @see #MODE_WORLD_READABLE
      * @see #MODE_WORLD_WRITEABLE
      */
-    public abstract SharedPreferences getSharedPreferences(String name,
-            int mode);
+    public abstract SharedPreferences getSharedPreferences(String name, int mode);
+
+    /**
+     * Retrieve and hold the contents of the preferences file, returning
+     * a SharedPreferences through which you can retrieve and modify its
+     * values.  Only one instance of the SharedPreferences object is returned
+     * to any callers for the same name, meaning they will see each other's
+     * edits as soon as they are made.
+     *
+     * @param file Desired preferences file. If a preferences file by this name
+     * does not exist, it will be created when you retrieve an
+     * editor (SharedPreferences.edit()) and then commit changes (Editor.commit()).
+     * @param mode Operating mode.  Use 0 or {@link #MODE_PRIVATE} for the
+     * default operation, {@link #MODE_WORLD_READABLE}
+     * and {@link #MODE_WORLD_WRITEABLE} to control permissions.
+     *
+     * @return The single {@link SharedPreferences} instance that can be used
+     *         to retrieve and modify the preference values.
+     *
+     * @see #MODE_PRIVATE
+     * @see #MODE_WORLD_READABLE
+     * @see #MODE_WORLD_WRITEABLE
+     */
+    public abstract SharedPreferences getSharedPreferences(File file, int mode);
 
     /**
      * Open a private file associated with this Context's application package
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index bec1b37..a345aae 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -172,6 +172,11 @@
     }
 
     @Override
+    public SharedPreferences getSharedPreferences(File file, int mode) {
+        return mBase.getSharedPreferences(file, mode);
+    }
+
+    @Override
     public FileInputStream openFileInput(String name)
         throws FileNotFoundException {
         return mBase.openFileInput(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 17af944..ecb1850 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -84,9 +84,10 @@
 import java.util.List;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicReference;
-import java.util.jar.StrictJarFile;
 import java.util.zip.ZipEntry;
 
+import android.util.jar.StrictJarFile;
+
 /**
  * Parser for package files (APKs) on disk. This supports apps packaged either
  * as a single "monolithic" APK, or apps packaged as a "cluster" of multiple
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/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..4723025 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)
@@ -929,9 +942,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/util/jar/StrictJarFile.java b/core/java/android/util/jar/StrictJarFile.java
new file mode 100644
index 0000000..fd57806
--- /dev/null
+++ b/core/java/android/util/jar/StrictJarFile.java
@@ -0,0 +1,427 @@
+/*
+ * 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.util.jar;
+
+import dalvik.system.CloseGuard;
+import java.io.ByteArrayInputStream;
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.RandomAccessFile;
+import java.security.cert.Certificate;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.zip.Inflater;
+import java.util.zip.InflaterInputStream;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import java.util.jar.JarFile;
+import java.util.jar.Manifest;
+import libcore.io.IoUtils;
+import libcore.io.Streams;
+
+/**
+ * A subset of the JarFile API implemented as a thin wrapper over
+ * system/core/libziparchive.
+ *
+ * @hide for internal use only. Not API compatible (or as forgiving) as
+ *        {@link java.util.jar.JarFile}
+ */
+public final class StrictJarFile {
+
+    private final long nativeHandle;
+
+    // NOTE: It's possible to share a file descriptor with the native
+    // code, at the cost of some additional complexity.
+    private final RandomAccessFile raf;
+
+    private final StrictJarManifest manifest;
+    private final StrictJarVerifier verifier;
+
+    private final boolean isSigned;
+
+    private final CloseGuard guard = CloseGuard.get();
+    private boolean closed;
+
+    public StrictJarFile(String fileName) throws IOException, SecurityException {
+        this.nativeHandle = nativeOpenJarFile(fileName);
+        this.raf = new RandomAccessFile(fileName, "r");
+
+        try {
+            // Read the MANIFEST and signature files up front and try to
+            // parse them. We never want to accept a JAR File with broken signatures
+            // or manifests, so it's best to throw as early as possible.
+            HashMap<String, byte[]> metaEntries = getMetaEntries();
+            this.manifest = new StrictJarManifest(metaEntries.get(JarFile.MANIFEST_NAME), true);
+            this.verifier = new StrictJarVerifier(fileName, manifest, metaEntries);
+            Set<String> files = manifest.getEntries().keySet();
+            for (String file : files) {
+                if (findEntry(file) == null) {
+                    throw new SecurityException(fileName + ": File " + file + " in manifest does not exist");
+                }
+            }
+
+            isSigned = verifier.readCertificates() && verifier.isSignedJar();
+        } catch (IOException | SecurityException e) {
+            nativeClose(this.nativeHandle);
+            IoUtils.closeQuietly(this.raf);
+            throw e;
+        }
+
+        guard.open("close");
+    }
+
+    public StrictJarManifest getManifest() {
+        return manifest;
+    }
+
+    public Iterator<ZipEntry> iterator() throws IOException {
+        return new EntryIterator(nativeHandle, "");
+    }
+
+    public ZipEntry findEntry(String name) {
+        return nativeFindEntry(nativeHandle, name);
+    }
+
+    /**
+     * Return all certificate chains for a given {@link ZipEntry} belonging to this jar.
+     * This method MUST be called only after fully exhausting the InputStream belonging
+     * to this entry.
+     *
+     * Returns {@code null} if this jar file isn't signed or if this method is
+     * called before the stream is processed.
+     */
+    public Certificate[][] getCertificateChains(ZipEntry ze) {
+        if (isSigned) {
+            return verifier.getCertificateChains(ze.getName());
+        }
+
+        return null;
+    }
+
+    /**
+     * Return all certificates for a given {@link ZipEntry} belonging to this jar.
+     * This method MUST be called only after fully exhausting the InputStream belonging
+     * to this entry.
+     *
+     * Returns {@code null} if this jar file isn't signed or if this method is
+     * called before the stream is processed.
+     *
+     * @deprecated Switch callers to use getCertificateChains instead
+     */
+    @Deprecated
+    public Certificate[] getCertificates(ZipEntry ze) {
+        if (isSigned) {
+            Certificate[][] certChains = verifier.getCertificateChains(ze.getName());
+
+            // Measure number of certs.
+            int count = 0;
+            for (Certificate[] chain : certChains) {
+                count += chain.length;
+            }
+
+            // Create new array and copy all the certs into it.
+            Certificate[] certs = new Certificate[count];
+            int i = 0;
+            for (Certificate[] chain : certChains) {
+                System.arraycopy(chain, 0, certs, i, chain.length);
+                i += chain.length;
+            }
+
+            return certs;
+        }
+
+        return null;
+    }
+
+    public InputStream getInputStream(ZipEntry ze) {
+        final InputStream is = getZipInputStream(ze);
+
+        if (isSigned) {
+            StrictJarVerifier.VerifierEntry entry = verifier.initEntry(ze.getName());
+            if (entry == null) {
+                return is;
+            }
+
+            return new JarFileInputStream(is, ze.getSize(), entry);
+        }
+
+        return is;
+    }
+
+    public void close() throws IOException {
+        if (!closed) {
+            guard.close();
+
+            nativeClose(nativeHandle);
+            IoUtils.closeQuietly(raf);
+            closed = true;
+        }
+    }
+
+    private InputStream getZipInputStream(ZipEntry ze) {
+        if (ze.getMethod() == ZipEntry.STORED) {
+            return new RAFStream(raf, ze.getDataOffset(),
+                    ze.getDataOffset() + ze.getSize());
+        } else {
+            final RAFStream wrapped = new RAFStream(
+                    raf, ze.getDataOffset(), ze.getDataOffset() + ze.getCompressedSize());
+
+            int bufSize = Math.max(1024, (int) Math.min(ze.getSize(), 65535L));
+            return new ZipInflaterInputStream(wrapped, new Inflater(true), bufSize, ze);
+        }
+    }
+
+    static final class EntryIterator implements Iterator<ZipEntry> {
+        private final long iterationHandle;
+        private ZipEntry nextEntry;
+
+        EntryIterator(long nativeHandle, String prefix) throws IOException {
+            iterationHandle = nativeStartIteration(nativeHandle, prefix);
+        }
+
+        public ZipEntry next() {
+            if (nextEntry != null) {
+                final ZipEntry ze = nextEntry;
+                nextEntry = null;
+                return ze;
+            }
+
+            return nativeNextEntry(iterationHandle);
+        }
+
+        public boolean hasNext() {
+            if (nextEntry != null) {
+                return true;
+            }
+
+            final ZipEntry ze = nativeNextEntry(iterationHandle);
+            if (ze == null) {
+                return false;
+            }
+
+            nextEntry = ze;
+            return true;
+        }
+
+        public void remove() {
+            throw new UnsupportedOperationException();
+        }
+    }
+
+    private HashMap<String, byte[]> getMetaEntries() throws IOException {
+        HashMap<String, byte[]> metaEntries = new HashMap<String, byte[]>();
+
+        Iterator<ZipEntry> entryIterator = new EntryIterator(nativeHandle, "META-INF/");
+        while (entryIterator.hasNext()) {
+            final ZipEntry entry = entryIterator.next();
+            metaEntries.put(entry.getName(), Streams.readFully(getInputStream(entry)));
+        }
+
+        return metaEntries;
+    }
+
+    static final class JarFileInputStream extends FilterInputStream {
+        private final StrictJarVerifier.VerifierEntry entry;
+
+        private long count;
+        private boolean done = false;
+
+        JarFileInputStream(InputStream is, long size, StrictJarVerifier.VerifierEntry e) {
+            super(is);
+            entry = e;
+
+            count = size;
+        }
+
+        @Override
+        public int read() throws IOException {
+            if (done) {
+                return -1;
+            }
+            if (count > 0) {
+                int r = super.read();
+                if (r != -1) {
+                    entry.write(r);
+                    count--;
+                } else {
+                    count = 0;
+                }
+                if (count == 0) {
+                    done = true;
+                    entry.verify();
+                }
+                return r;
+            } else {
+                done = true;
+                entry.verify();
+                return -1;
+            }
+        }
+
+        @Override
+        public int read(byte[] buffer, int byteOffset, int byteCount) throws IOException {
+            if (done) {
+                return -1;
+            }
+            if (count > 0) {
+                int r = super.read(buffer, byteOffset, byteCount);
+                if (r != -1) {
+                    int size = r;
+                    if (count < size) {
+                        size = (int) count;
+                    }
+                    entry.write(buffer, byteOffset, size);
+                    count -= size;
+                } else {
+                    count = 0;
+                }
+                if (count == 0) {
+                    done = true;
+                    entry.verify();
+                }
+                return r;
+            } else {
+                done = true;
+                entry.verify();
+                return -1;
+            }
+        }
+
+        @Override
+        public int available() throws IOException {
+            if (done) {
+                return 0;
+            }
+            return super.available();
+        }
+
+        @Override
+        public long skip(long byteCount) throws IOException {
+            return Streams.skipByReading(this, byteCount);
+        }
+    }
+
+    /** @hide */
+    public static class ZipInflaterInputStream extends InflaterInputStream {
+        private final ZipEntry entry;
+        private long bytesRead = 0;
+
+        public ZipInflaterInputStream(InputStream is, Inflater inf, int bsize, ZipEntry entry) {
+            super(is, inf, bsize);
+            this.entry = entry;
+        }
+
+        @Override public int read(byte[] buffer, int byteOffset, int byteCount) throws IOException {
+            final int i;
+            try {
+                i = super.read(buffer, byteOffset, byteCount);
+            } catch (IOException e) {
+                throw new IOException("Error reading data for " + entry.getName() + " near offset "
+                        + bytesRead, e);
+            }
+            if (i == -1) {
+                if (entry.getSize() != bytesRead) {
+                    throw new IOException("Size mismatch on inflated file: " + bytesRead + " vs "
+                            + entry.getSize());
+                }
+            } else {
+                bytesRead += i;
+            }
+            return i;
+        }
+
+        @Override public int available() throws IOException {
+            if (closed) {
+                // Our superclass will throw an exception, but there's a jtreg test that
+                // explicitly checks that the InputStream returned from ZipFile.getInputStream
+                // returns 0 even when closed.
+                return 0;
+            }
+            return super.available() == 0 ? 0 : (int) (entry.getSize() - bytesRead);
+        }
+    }
+
+    /**
+     * Wrap a stream around a RandomAccessFile.  The RandomAccessFile is shared
+     * among all streams returned by getInputStream(), so we have to synchronize
+     * access to it.  (We can optimize this by adding buffering here to reduce
+     * collisions.)
+     *
+     * <p>We could support mark/reset, but we don't currently need them.
+     *
+     * @hide
+     */
+    public static class RAFStream extends InputStream {
+        private final RandomAccessFile sharedRaf;
+        private long endOffset;
+        private long offset;
+
+
+        public RAFStream(RandomAccessFile raf, long initialOffset, long endOffset) {
+            sharedRaf = raf;
+            offset = initialOffset;
+            this.endOffset = endOffset;
+        }
+
+        public RAFStream(RandomAccessFile raf, long initialOffset) throws IOException {
+            this(raf, initialOffset, raf.length());
+        }
+
+        @Override public int available() throws IOException {
+            return (offset < endOffset ? 1 : 0);
+        }
+
+        @Override public int read() throws IOException {
+            return Streams.readSingleByte(this);
+        }
+
+        @Override public int read(byte[] buffer, int byteOffset, int byteCount) throws IOException {
+            synchronized (sharedRaf) {
+                final long length = endOffset - offset;
+                if (byteCount > length) {
+                    byteCount = (int) length;
+                }
+                sharedRaf.seek(offset);
+                int count = sharedRaf.read(buffer, byteOffset, byteCount);
+                if (count > 0) {
+                    offset += count;
+                    return count;
+                } else {
+                    return -1;
+                }
+            }
+        }
+
+        @Override public long skip(long byteCount) throws IOException {
+            if (byteCount > endOffset - offset) {
+                byteCount = endOffset - offset;
+            }
+            offset += byteCount;
+            return byteCount;
+        }
+    }
+
+
+    private static native long nativeOpenJarFile(String fileName) throws IOException;
+    private static native long nativeStartIteration(long nativeHandle, String prefix);
+    private static native ZipEntry nativeNextEntry(long iterationHandle);
+    private static native ZipEntry nativeFindEntry(long nativeHandle, String entryName);
+    private static native void nativeClose(long nativeHandle);
+}
diff --git a/core/java/android/util/jar/StrictJarManifest.java b/core/java/android/util/jar/StrictJarManifest.java
new file mode 100644
index 0000000..dbb466c
--- /dev/null
+++ b/core/java/android/util/jar/StrictJarManifest.java
@@ -0,0 +1,315 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.util.jar;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.CharsetEncoder;
+import java.nio.charset.CoderResult;
+import java.nio.charset.StandardCharsets;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.jar.Attributes;
+import libcore.io.Streams;
+
+/**
+ * The {@code StrictJarManifest} class is used to obtain attribute information for a
+ * {@code StrictJarFile} and its entries.
+ *
+ * @hide
+ */
+public class StrictJarManifest implements Cloneable {
+    static final int LINE_LENGTH_LIMIT = 72;
+
+    private static final byte[] LINE_SEPARATOR = new byte[] { '\r', '\n' };
+
+    private static final byte[] VALUE_SEPARATOR = new byte[] { ':', ' ' };
+
+    private final Attributes mainAttributes;
+    private final HashMap<String, Attributes> entries;
+
+    static final class Chunk {
+        final int start;
+        final int end;
+
+        Chunk(int start, int end) {
+            this.start = start;
+            this.end = end;
+        }
+    }
+
+    private HashMap<String, Chunk> chunks;
+
+    /**
+     * The end of the main attributes section in the manifest is needed in
+     * verification.
+     */
+    private int mainEnd;
+
+    /**
+     * Creates a new {@code StrictJarManifest} instance.
+     */
+    public StrictJarManifest() {
+        entries = new HashMap<String, Attributes>();
+        mainAttributes = new Attributes();
+    }
+
+    /**
+     * Creates a new {@code StrictJarManifest} instance using the attributes obtained
+     * from the input stream.
+     *
+     * @param is
+     *            {@code InputStream} to parse for attributes.
+     * @throws IOException
+     *             if an IO error occurs while creating this {@code StrictJarManifest}
+     */
+    public StrictJarManifest(InputStream is) throws IOException {
+        this();
+        read(Streams.readFully(is));
+    }
+
+    /**
+     * Creates a new {@code StrictJarManifest} instance. The new instance will have the
+     * same attributes as those found in the parameter {@code StrictJarManifest}.
+     *
+     * @param man
+     *            {@code StrictJarManifest} instance to obtain attributes from.
+     */
+    @SuppressWarnings("unchecked")
+    public StrictJarManifest(StrictJarManifest man) {
+        mainAttributes = (Attributes) man.mainAttributes.clone();
+        entries = (HashMap<String, Attributes>) ((HashMap<String, Attributes>) man
+                .getEntries()).clone();
+    }
+
+    StrictJarManifest(byte[] manifestBytes, boolean readChunks) throws IOException {
+        this();
+        if (readChunks) {
+            chunks = new HashMap<String, Chunk>();
+        }
+        read(manifestBytes);
+    }
+
+    /**
+     * Resets the both the main attributes as well as the entry attributes
+     * associated with this {@code StrictJarManifest}.
+     */
+    public void clear() {
+        entries.clear();
+        mainAttributes.clear();
+    }
+
+    /**
+     * Returns the {@code Attributes} associated with the parameter entry
+     * {@code name}.
+     *
+     * @param name
+     *            the name of the entry to obtain {@code Attributes} from.
+     * @return the Attributes for the entry or {@code null} if the entry does
+     *         not exist.
+     */
+    public Attributes getAttributes(String name) {
+        return getEntries().get(name);
+    }
+
+    /**
+     * Returns a map containing the {@code Attributes} for each entry in the
+     * {@code StrictJarManifest}.
+     *
+     * @return the map of entry attributes.
+     */
+    public Map<String, Attributes> getEntries() {
+        return entries;
+    }
+
+    /**
+     * Returns the main {@code Attributes} of the {@code JarFile}.
+     *
+     * @return main {@code Attributes} associated with the source {@code
+     *         JarFile}.
+     */
+    public Attributes getMainAttributes() {
+        return mainAttributes;
+    }
+
+    /**
+     * Creates a copy of this {@code StrictJarManifest}. The returned {@code StrictJarManifest}
+     * will equal the {@code StrictJarManifest} from which it was cloned.
+     *
+     * @return a copy of this instance.
+     */
+    @Override
+    public Object clone() {
+        return new StrictJarManifest(this);
+    }
+
+    /**
+     * Writes this {@code StrictJarManifest}'s name/attributes pairs to the given {@code OutputStream}.
+     * The {@code MANIFEST_VERSION} or {@code SIGNATURE_VERSION} attribute must be set before
+     * calling this method, or no attributes will be written.
+     *
+     * @throws IOException
+     *             If an error occurs writing the {@code StrictJarManifest}.
+     */
+    public void write(OutputStream os) throws IOException {
+        write(this, os);
+    }
+
+    /**
+     * Merges name/attribute pairs read from the input stream {@code is} into this manifest.
+     *
+     * @param is
+     *            The {@code InputStream} to read from.
+     * @throws IOException
+     *             If an error occurs reading the manifest.
+     */
+    public void read(InputStream is) throws IOException {
+        read(Streams.readFullyNoClose(is));
+    }
+
+    private void read(byte[] buf) throws IOException {
+        if (buf.length == 0) {
+            return;
+        }
+
+        StrictJarManifestReader im = new StrictJarManifestReader(buf, mainAttributes);
+        mainEnd = im.getEndOfMainSection();
+        im.readEntries(entries, chunks);
+    }
+
+    /**
+     * Returns the hash code for this instance.
+     *
+     * @return this {@code StrictJarManifest}'s hashCode.
+     */
+    @Override
+    public int hashCode() {
+        return mainAttributes.hashCode() ^ getEntries().hashCode();
+    }
+
+    /**
+     * Determines if the receiver is equal to the parameter object. Two {@code
+     * StrictJarManifest}s are equal if they have identical main attributes as well as
+     * identical entry attributes.
+     *
+     * @param o
+     *            the object to compare against.
+     * @return {@code true} if the manifests are equal, {@code false} otherwise
+     */
+    @Override
+    public boolean equals(Object o) {
+        if (o == null) {
+            return false;
+        }
+        if (o.getClass() != this.getClass()) {
+            return false;
+        }
+        if (!mainAttributes.equals(((StrictJarManifest) o).mainAttributes)) {
+            return false;
+        }
+        return getEntries().equals(((StrictJarManifest) o).getEntries());
+    }
+
+    Chunk getChunk(String name) {
+        return chunks.get(name);
+    }
+
+    void removeChunks() {
+        chunks = null;
+    }
+
+    int getMainAttributesEnd() {
+        return mainEnd;
+    }
+
+    /**
+     * Writes out the attribute information of the specified manifest to the
+     * specified {@code OutputStream}
+     *
+     * @param manifest
+     *            the manifest to write out.
+     * @param out
+     *            The {@code OutputStream} to write to.
+     * @throws IOException
+     *             If an error occurs writing the {@code StrictJarManifest}.
+     */
+    static void write(StrictJarManifest manifest, OutputStream out) throws IOException {
+        CharsetEncoder encoder = StandardCharsets.UTF_8.newEncoder();
+        ByteBuffer buffer = ByteBuffer.allocate(LINE_LENGTH_LIMIT);
+
+        Attributes.Name versionName = Attributes.Name.MANIFEST_VERSION;
+        String version = manifest.mainAttributes.getValue(versionName);
+        if (version == null) {
+            versionName = Attributes.Name.SIGNATURE_VERSION;
+            version = manifest.mainAttributes.getValue(versionName);
+        }
+        if (version != null) {
+            writeEntry(out, versionName, version, encoder, buffer);
+            Iterator<?> entries = manifest.mainAttributes.keySet().iterator();
+            while (entries.hasNext()) {
+                Attributes.Name name = (Attributes.Name) entries.next();
+                if (!name.equals(versionName)) {
+                    writeEntry(out, name, manifest.mainAttributes.getValue(name), encoder, buffer);
+                }
+            }
+        }
+        out.write(LINE_SEPARATOR);
+        Iterator<String> i = manifest.getEntries().keySet().iterator();
+        while (i.hasNext()) {
+            String key = i.next();
+            writeEntry(out, Attributes.Name.NAME, key, encoder, buffer);
+            Attributes attributes = manifest.entries.get(key);
+            Iterator<?> entries = attributes.keySet().iterator();
+            while (entries.hasNext()) {
+                Attributes.Name name = (Attributes.Name) entries.next();
+                writeEntry(out, name, attributes.getValue(name), encoder, buffer);
+            }
+            out.write(LINE_SEPARATOR);
+        }
+    }
+
+    private static void writeEntry(OutputStream os, Attributes.Name name,
+            String value, CharsetEncoder encoder, ByteBuffer bBuf) throws IOException {
+        String nameString = name.toString();
+        os.write(nameString.getBytes(StandardCharsets.US_ASCII));
+        os.write(VALUE_SEPARATOR);
+
+        encoder.reset();
+        bBuf.clear().limit(LINE_LENGTH_LIMIT - nameString.length() - 2);
+
+        CharBuffer cBuf = CharBuffer.wrap(value);
+
+        while (true) {
+            CoderResult r = encoder.encode(cBuf, bBuf, true);
+            if (CoderResult.UNDERFLOW == r) {
+                r = encoder.flush(bBuf);
+            }
+            os.write(bBuf.array(), bBuf.arrayOffset(), bBuf.position());
+            os.write(LINE_SEPARATOR);
+            if (CoderResult.UNDERFLOW == r) {
+                break;
+            }
+            os.write(' ');
+            bBuf.clear().limit(LINE_LENGTH_LIMIT - 1);
+        }
+    }
+}
diff --git a/core/java/android/util/jar/StrictJarManifestReader.java b/core/java/android/util/jar/StrictJarManifestReader.java
new file mode 100644
index 0000000..9881bb0
--- /dev/null
+++ b/core/java/android/util/jar/StrictJarManifestReader.java
@@ -0,0 +1,184 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.util.jar;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.jar.Attributes;
+
+/**
+ * Reads a JAR file manifest. The specification is here:
+ * http://java.sun.com/javase/6/docs/technotes/guides/jar/jar.html
+ */
+class StrictJarManifestReader {
+    // There are relatively few unique attribute names,
+    // but a manifest might have thousands of entries.
+    private final HashMap<String, Attributes.Name> attributeNameCache = new HashMap<String, Attributes.Name>();
+
+    private final ByteArrayOutputStream valueBuffer = new ByteArrayOutputStream(80);
+
+    private final byte[] buf;
+
+    private final int endOfMainSection;
+
+    private int pos;
+
+    private Attributes.Name name;
+
+    private String value;
+
+    private int consecutiveLineBreaks = 0;
+
+    public StrictJarManifestReader(byte[] buf, Attributes main) throws IOException {
+        this.buf = buf;
+        while (readHeader()) {
+            main.put(name, value);
+        }
+        this.endOfMainSection = pos;
+    }
+
+    public void readEntries(Map<String, Attributes> entries, Map<String, StrictJarManifest.Chunk> chunks) throws IOException {
+        int mark = pos;
+        while (readHeader()) {
+            if (!Attributes.Name.NAME.equals(name)) {
+                throw new IOException("Entry is not named");
+            }
+            String entryNameValue = value;
+
+            Attributes entry = entries.get(entryNameValue);
+            if (entry == null) {
+                entry = new Attributes(12);
+            }
+
+            while (readHeader()) {
+                entry.put(name, value);
+            }
+
+            if (chunks != null) {
+                if (chunks.get(entryNameValue) != null) {
+                    // TODO A bug: there might be several verification chunks for
+                    // the same name. I believe they should be used to update
+                    // signature in order of appearance; there are two ways to fix
+                    // this: either use a list of chunks, or decide on used
+                    // signature algorithm in advance and reread the chunks while
+                    // updating the signature; for now a defensive error is thrown
+                    throw new IOException("A jar verifier does not support more than one entry with the same name");
+                }
+                chunks.put(entryNameValue, new StrictJarManifest.Chunk(mark, pos));
+                mark = pos;
+            }
+
+            entries.put(entryNameValue, entry);
+        }
+    }
+
+    public int getEndOfMainSection() {
+        return endOfMainSection;
+    }
+
+    /**
+     * Read a single line from the manifest buffer.
+     */
+    private boolean readHeader() throws IOException {
+        if (consecutiveLineBreaks > 1) {
+            // break a section on an empty line
+            consecutiveLineBreaks = 0;
+            return false;
+        }
+        readName();
+        consecutiveLineBreaks = 0;
+        readValue();
+        // if the last line break is missed, the line
+        // is ignored by the reference implementation
+        return consecutiveLineBreaks > 0;
+    }
+
+    private void readName() throws IOException {
+        int mark = pos;
+
+        while (pos < buf.length) {
+            if (buf[pos++] != ':') {
+                continue;
+            }
+
+            String nameString = new String(buf, mark, pos - mark - 1, StandardCharsets.US_ASCII);
+
+            if (buf[pos++] != ' ') {
+                throw new IOException(String.format("Invalid value for attribute '%s'", nameString));
+            }
+
+            try {
+                name = attributeNameCache.get(nameString);
+                if (name == null) {
+                    name = new Attributes.Name(nameString);
+                    attributeNameCache.put(nameString, name);
+                }
+            } catch (IllegalArgumentException e) {
+                // new Attributes.Name() throws IllegalArgumentException but we declare IOException
+                throw new IOException(e.getMessage());
+            }
+            return;
+        }
+    }
+
+    private void readValue() throws IOException {
+        boolean lastCr = false;
+        int mark = pos;
+        int last = pos;
+        valueBuffer.reset();
+        while (pos < buf.length) {
+            byte next = buf[pos++];
+            switch (next) {
+            case 0:
+                throw new IOException("NUL character in a manifest");
+            case '\n':
+                if (lastCr) {
+                    lastCr = false;
+                } else {
+                    consecutiveLineBreaks++;
+                }
+                continue;
+            case '\r':
+                lastCr = true;
+                consecutiveLineBreaks++;
+                continue;
+            case ' ':
+                if (consecutiveLineBreaks == 1) {
+                    valueBuffer.write(buf, mark, last - mark);
+                    mark = pos;
+                    consecutiveLineBreaks = 0;
+                    continue;
+                }
+            }
+
+            if (consecutiveLineBreaks >= 1) {
+                pos--;
+                break;
+            }
+            last = pos;
+        }
+
+        valueBuffer.write(buf, mark, last - mark);
+        // A bit frustrating that that Charset.forName will be called
+        // again.
+        value = valueBuffer.toString(StandardCharsets.UTF_8.name());
+    }
+}
diff --git a/core/java/android/util/jar/StrictJarVerifier.java b/core/java/android/util/jar/StrictJarVerifier.java
new file mode 100644
index 0000000..ca2aec1
--- /dev/null
+++ b/core/java/android/util/jar/StrictJarVerifier.java
@@ -0,0 +1,456 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.util.jar;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
+import java.security.GeneralSecurityException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.Certificate;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.Map;
+import java.util.jar.Attributes;
+import java.util.jar.JarFile;
+import libcore.io.Base64;
+import sun.security.jca.Providers;
+import sun.security.pkcs.PKCS7;
+
+/**
+ * Non-public class used by {@link JarFile} and {@link JarInputStream} to manage
+ * the verification of signed JARs. {@code JarFile} and {@code JarInputStream}
+ * objects are expected to have a {@code JarVerifier} instance member which
+ * can be used to carry out the tasks associated with verifying a signed JAR.
+ * These tasks would typically include:
+ * <ul>
+ * <li>verification of all signed signature files
+ * <li>confirmation that all signed data was signed only by the party or parties
+ * specified in the signature block data
+ * <li>verification that the contents of all signature files (i.e. {@code .SF}
+ * files) agree with the JAR entries information found in the JAR manifest.
+ * </ul>
+ */
+class StrictJarVerifier {
+    /**
+     * List of accepted digest algorithms. This list is in order from most
+     * preferred to least preferred.
+     */
+    private static final String[] DIGEST_ALGORITHMS = new String[] {
+        "SHA-512",
+        "SHA-384",
+        "SHA-256",
+        "SHA1",
+    };
+
+    private final String jarName;
+    private final StrictJarManifest manifest;
+    private final HashMap<String, byte[]> metaEntries;
+    private final int mainAttributesEnd;
+
+    private final Hashtable<String, HashMap<String, Attributes>> signatures =
+            new Hashtable<String, HashMap<String, Attributes>>(5);
+
+    private final Hashtable<String, Certificate[]> certificates =
+            new Hashtable<String, Certificate[]>(5);
+
+    private final Hashtable<String, Certificate[][]> verifiedEntries =
+            new Hashtable<String, Certificate[][]>();
+
+    /**
+     * Stores and a hash and a message digest and verifies that massage digest
+     * matches the hash.
+     */
+    static class VerifierEntry extends OutputStream {
+
+        private final String name;
+
+        private final MessageDigest digest;
+
+        private final byte[] hash;
+
+        private final Certificate[][] certChains;
+
+        private final Hashtable<String, Certificate[][]> verifiedEntries;
+
+        VerifierEntry(String name, MessageDigest digest, byte[] hash,
+                Certificate[][] certChains, Hashtable<String, Certificate[][]> verifedEntries) {
+            this.name = name;
+            this.digest = digest;
+            this.hash = hash;
+            this.certChains = certChains;
+            this.verifiedEntries = verifedEntries;
+        }
+
+        /**
+         * Updates a digest with one byte.
+         */
+        @Override
+        public void write(int value) {
+            digest.update((byte) value);
+        }
+
+        /**
+         * Updates a digest with byte array.
+         */
+        @Override
+        public void write(byte[] buf, int off, int nbytes) {
+            digest.update(buf, off, nbytes);
+        }
+
+        /**
+         * Verifies that the digests stored in the manifest match the decrypted
+         * digests from the .SF file. This indicates the validity of the
+         * signing, not the integrity of the file, as its digest must be
+         * calculated and verified when its contents are read.
+         *
+         * @throws SecurityException
+         *             if the digest value stored in the manifest does <i>not</i>
+         *             agree with the decrypted digest as recovered from the
+         *             <code>.SF</code> file.
+         */
+        void verify() {
+            byte[] d = digest.digest();
+            if (!MessageDigest.isEqual(d, Base64.decode(hash))) {
+                throw invalidDigest(JarFile.MANIFEST_NAME, name, name);
+            }
+            verifiedEntries.put(name, certChains);
+        }
+    }
+
+    private static SecurityException invalidDigest(String signatureFile, String name,
+            String jarName) {
+        throw new SecurityException(signatureFile + " has invalid digest for " + name +
+                " in " + jarName);
+    }
+
+    private static SecurityException failedVerification(String jarName, String signatureFile) {
+        throw new SecurityException(jarName + " failed verification of " + signatureFile);
+    }
+
+    private static SecurityException failedVerification(String jarName, String signatureFile,
+                                                      Throwable e) {
+        throw new SecurityException(jarName + " failed verification of " + signatureFile, e);
+    }
+
+
+    /**
+     * Constructs and returns a new instance of {@code JarVerifier}.
+     *
+     * @param name
+     *            the name of the JAR file being verified.
+     */
+    StrictJarVerifier(String name, StrictJarManifest manifest,
+        HashMap<String, byte[]> metaEntries) {
+        jarName = name;
+        this.manifest = manifest;
+        this.metaEntries = metaEntries;
+        this.mainAttributesEnd = manifest.getMainAttributesEnd();
+    }
+
+    /**
+     * Invoked for each new JAR entry read operation from the input
+     * stream. This method constructs and returns a new {@link VerifierEntry}
+     * which contains the certificates used to sign the entry and its hash value
+     * as specified in the JAR MANIFEST format.
+     *
+     * @param name
+     *            the name of an entry in a JAR file which is <b>not</b> in the
+     *            {@code META-INF} directory.
+     * @return a new instance of {@link VerifierEntry} which can be used by
+     *         callers as an {@link OutputStream}.
+     */
+    VerifierEntry initEntry(String name) {
+        // If no manifest is present by the time an entry is found,
+        // verification cannot occur. If no signature files have
+        // been found, do not verify.
+        if (manifest == null || signatures.isEmpty()) {
+            return null;
+        }
+
+        Attributes attributes = manifest.getAttributes(name);
+        // entry has no digest
+        if (attributes == null) {
+            return null;
+        }
+
+        ArrayList<Certificate[]> certChains = new ArrayList<Certificate[]>();
+        Iterator<Map.Entry<String, HashMap<String, Attributes>>> it = signatures.entrySet().iterator();
+        while (it.hasNext()) {
+            Map.Entry<String, HashMap<String, Attributes>> entry = it.next();
+            HashMap<String, Attributes> hm = entry.getValue();
+            if (hm.get(name) != null) {
+                // Found an entry for entry name in .SF file
+                String signatureFile = entry.getKey();
+                Certificate[] certChain = certificates.get(signatureFile);
+                if (certChain != null) {
+                    certChains.add(certChain);
+                }
+            }
+        }
+
+        // entry is not signed
+        if (certChains.isEmpty()) {
+            return null;
+        }
+        Certificate[][] certChainsArray = certChains.toArray(new Certificate[certChains.size()][]);
+
+        for (int i = 0; i < DIGEST_ALGORITHMS.length; i++) {
+            final String algorithm = DIGEST_ALGORITHMS[i];
+            final String hash = attributes.getValue(algorithm + "-Digest");
+            if (hash == null) {
+                continue;
+            }
+            byte[] hashBytes = hash.getBytes(StandardCharsets.ISO_8859_1);
+
+            try {
+                return new VerifierEntry(name, MessageDigest.getInstance(algorithm), hashBytes,
+                        certChainsArray, verifiedEntries);
+            } catch (NoSuchAlgorithmException ignored) {
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Add a new meta entry to the internal collection of data held on each JAR
+     * entry in the {@code META-INF} directory including the manifest
+     * file itself. Files associated with the signing of a JAR would also be
+     * added to this collection.
+     *
+     * @param name
+     *            the name of the file located in the {@code META-INF}
+     *            directory.
+     * @param buf
+     *            the file bytes for the file called {@code name}.
+     * @see #removeMetaEntries()
+     */
+    void addMetaEntry(String name, byte[] buf) {
+        metaEntries.put(name.toUpperCase(Locale.US), buf);
+    }
+
+    /**
+     * If the associated JAR file is signed, check on the validity of all of the
+     * known signatures.
+     *
+     * @return {@code true} if the associated JAR is signed and an internal
+     *         check verifies the validity of the signature(s). {@code false} if
+     *         the associated JAR file has no entries at all in its {@code
+     *         META-INF} directory. This situation is indicative of an invalid
+     *         JAR file.
+     *         <p>
+     *         Will also return {@code true} if the JAR file is <i>not</i>
+     *         signed.
+     * @throws SecurityException
+     *             if the JAR file is signed and it is determined that a
+     *             signature block file contains an invalid signature for the
+     *             corresponding signature file.
+     */
+    synchronized boolean readCertificates() {
+        if (metaEntries.isEmpty()) {
+            return false;
+        }
+
+        Iterator<String> it = metaEntries.keySet().iterator();
+        while (it.hasNext()) {
+            String key = it.next();
+            if (key.endsWith(".DSA") || key.endsWith(".RSA") || key.endsWith(".EC")) {
+                verifyCertificate(key);
+                it.remove();
+            }
+        }
+        return true;
+    }
+
+   /**
+     * Verifies that the signature computed from {@code sfBytes} matches
+     * that specified in {@code blockBytes} (which is a PKCS7 block). Returns
+     * certificates listed in the PKCS7 block. Throws a {@code GeneralSecurityException}
+     * if something goes wrong during verification.
+     */
+    static Certificate[] verifyBytes(byte[] blockBytes, byte[] sfBytes)
+        throws GeneralSecurityException {
+
+        Object obj = null;
+        try {
+
+            obj = Providers.startJarVerification();
+            PKCS7 block = new PKCS7(blockBytes);
+            if (block.verify(sfBytes) == null) {
+                throw new GeneralSecurityException("Failed to verify signature");
+            }
+            X509Certificate[] blockCerts = block.getCertificates();
+            Certificate[] signerCertChain = null;
+            if (blockCerts != null) {
+                signerCertChain = new Certificate[blockCerts.length];
+                for (int i = 0; i < blockCerts.length; ++i) {
+                    signerCertChain[i] = blockCerts[i];
+                }
+            }
+            return signerCertChain;
+        } catch (IOException e) {
+            throw new GeneralSecurityException("IO exception verifying jar cert", e);
+        } finally {
+            Providers.stopJarVerification(obj);
+        }
+    }
+
+    /**
+     * @param certFile
+     */
+    private void verifyCertificate(String certFile) {
+        // Found Digital Sig, .SF should already have been read
+        String signatureFile = certFile.substring(0, certFile.lastIndexOf('.')) + ".SF";
+        byte[] sfBytes = metaEntries.get(signatureFile);
+        if (sfBytes == null) {
+            return;
+        }
+
+        byte[] manifestBytes = metaEntries.get(JarFile.MANIFEST_NAME);
+        // Manifest entry is required for any verifications.
+        if (manifestBytes == null) {
+            return;
+        }
+
+        byte[] sBlockBytes = metaEntries.get(certFile);
+        try {
+            Certificate[] signerCertChain = verifyBytes(sBlockBytes, sfBytes);
+            if (signerCertChain != null) {
+                certificates.put(signatureFile, signerCertChain);
+            }
+        } catch (GeneralSecurityException e) {
+          throw failedVerification(jarName, signatureFile, e);
+        }
+
+        // Verify manifest hash in .sf file
+        Attributes attributes = new Attributes();
+        HashMap<String, Attributes> entries = new HashMap<String, Attributes>();
+        try {
+            StrictJarManifestReader im = new StrictJarManifestReader(sfBytes, attributes);
+            im.readEntries(entries, null);
+        } catch (IOException e) {
+            return;
+        }
+
+        // Do we actually have any signatures to look at?
+        if (attributes.get(Attributes.Name.SIGNATURE_VERSION) == null) {
+            return;
+        }
+
+        boolean createdBySigntool = false;
+        String createdBy = attributes.getValue("Created-By");
+        if (createdBy != null) {
+            createdBySigntool = createdBy.indexOf("signtool") != -1;
+        }
+
+        // Use .SF to verify the mainAttributes of the manifest
+        // If there is no -Digest-Manifest-Main-Attributes entry in .SF
+        // file, such as those created before java 1.5, then we ignore
+        // such verification.
+        if (mainAttributesEnd > 0 && !createdBySigntool) {
+            String digestAttribute = "-Digest-Manifest-Main-Attributes";
+            if (!verify(attributes, digestAttribute, manifestBytes, 0, mainAttributesEnd, false, true)) {
+                throw failedVerification(jarName, signatureFile);
+            }
+        }
+
+        // Use .SF to verify the whole manifest.
+        String digestAttribute = createdBySigntool ? "-Digest" : "-Digest-Manifest";
+        if (!verify(attributes, digestAttribute, manifestBytes, 0, manifestBytes.length, false, false)) {
+            Iterator<Map.Entry<String, Attributes>> it = entries.entrySet().iterator();
+            while (it.hasNext()) {
+                Map.Entry<String, Attributes> entry = it.next();
+                StrictJarManifest.Chunk chunk = manifest.getChunk(entry.getKey());
+                if (chunk == null) {
+                    return;
+                }
+                if (!verify(entry.getValue(), "-Digest", manifestBytes,
+                        chunk.start, chunk.end, createdBySigntool, false)) {
+                    throw invalidDigest(signatureFile, entry.getKey(), jarName);
+                }
+            }
+        }
+        metaEntries.put(signatureFile, null);
+        signatures.put(signatureFile, entries);
+    }
+
+    /**
+     * Returns a <code>boolean</code> indication of whether or not the
+     * associated jar file is signed.
+     *
+     * @return {@code true} if the JAR is signed, {@code false}
+     *         otherwise.
+     */
+    boolean isSignedJar() {
+        return certificates.size() > 0;
+    }
+
+    private boolean verify(Attributes attributes, String entry, byte[] data,
+            int start, int end, boolean ignoreSecondEndline, boolean ignorable) {
+        for (int i = 0; i < DIGEST_ALGORITHMS.length; i++) {
+            String algorithm = DIGEST_ALGORITHMS[i];
+            String hash = attributes.getValue(algorithm + entry);
+            if (hash == null) {
+                continue;
+            }
+
+            MessageDigest md;
+            try {
+                md = MessageDigest.getInstance(algorithm);
+            } catch (NoSuchAlgorithmException e) {
+                continue;
+            }
+            if (ignoreSecondEndline && data[end - 1] == '\n' && data[end - 2] == '\n') {
+                md.update(data, start, end - 1 - start);
+            } else {
+                md.update(data, start, end - start);
+            }
+            byte[] b = md.digest();
+            byte[] hashBytes = hash.getBytes(StandardCharsets.ISO_8859_1);
+            return MessageDigest.isEqual(b, Base64.decode(hashBytes));
+        }
+        return ignorable;
+    }
+
+    /**
+     * Returns all of the {@link java.security.cert.Certificate} chains that
+     * were used to verify the signature on the JAR entry called
+     * {@code name}. Callers must not modify the returned arrays.
+     *
+     * @param name
+     *            the name of a JAR entry.
+     * @return an array of {@link java.security.cert.Certificate} chains.
+     */
+    Certificate[][] getCertificateChains(String name) {
+        return verifiedEntries.get(name);
+    }
+
+    /**
+     * Remove all entries from the internal collection of data held about each
+     * JAR entry in the {@code META-INF} directory.
+     */
+    void removeMetaEntries() {
+        metaEntries.clear();
+    }
+}
diff --git a/core/java/android/view/NotificationHeaderView.java b/core/java/android/view/NotificationHeaderView.java
new file mode 100644
index 0000000..82f6c7f
--- /dev/null
+++ b/core/java/android/view/NotificationHeaderView.java
@@ -0,0 +1,250 @@
+/*
+ * 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.view;
+
+import android.annotation.Nullable;
+import android.app.Notification;
+import android.content.Context;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.widget.LinearLayout;
+import android.widget.RemoteViews;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+
+/**
+ * A header of a notification view
+ *
+ * @hide
+ */
+@RemoteViews.RemoteView
+public class NotificationHeaderView extends LinearLayout {
+    private final int mHeaderMinWidth;
+    private View mAppName;
+    private View mSubTextView;
+    private OnClickListener mExpandClickListener;
+    private HeaderTouchListener mTouchListener = new HeaderTouchListener();
+    private View mExpandButton;
+    private View mIcon;
+    private TextView mChildCount;
+
+    public NotificationHeaderView(Context context) {
+        this(context, null);
+    }
+
+    public NotificationHeaderView(Context context, @Nullable AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public NotificationHeaderView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public NotificationHeaderView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+        mHeaderMinWidth = getResources().getDimensionPixelSize(
+                com.android.internal.R.dimen.notification_header_shrink_min_width);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mAppName = findViewById(com.android.internal.R.id.app_name_text);
+        mSubTextView = findViewById(com.android.internal.R.id.header_sub_text);
+        mExpandButton = findViewById(com.android.internal.R.id.expand_button);
+        mIcon = findViewById(com.android.internal.R.id.icon);
+        mChildCount = (TextView) findViewById(com.android.internal.R.id.number_of_children);
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        final int givenWidth = MeasureSpec.getSize(widthMeasureSpec);
+        final int givenHeight = MeasureSpec.getSize(heightMeasureSpec);
+        int wrapContentWidthSpec = MeasureSpec.makeMeasureSpec(givenWidth,
+                MeasureSpec.AT_MOST);
+        int wrapContentHeightSpec = MeasureSpec.makeMeasureSpec(givenHeight,
+                MeasureSpec.AT_MOST);
+        int totalWidth = getPaddingStart() + getPaddingEnd();
+        for (int i = 0; i < getChildCount(); i++) {
+            final View child = getChildAt(i);
+            if (child.getVisibility() == GONE) {
+                // We'll give it the rest of the space in the end
+                continue;
+            }
+            final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
+            int childWidthSpec = getChildMeasureSpec(wrapContentWidthSpec,
+                    lp.leftMargin + lp.rightMargin, lp.width);
+            int childHeightSpec = getChildMeasureSpec(wrapContentHeightSpec,
+                    lp.topMargin + lp.bottomMargin, lp.height);
+            child.measure(childWidthSpec, childHeightSpec);
+            totalWidth += lp.leftMargin + lp.rightMargin + child.getMeasuredWidth();
+        }
+        if (totalWidth > givenWidth) {
+            int overFlow = totalWidth - givenWidth;
+            // We are overflowing, lets shrink
+            final int appWidth = mAppName.getMeasuredWidth();
+            if (appWidth > mHeaderMinWidth) {
+                int newSize = appWidth - Math.min(appWidth - mHeaderMinWidth, overFlow);
+                int childWidthSpec = MeasureSpec.makeMeasureSpec(newSize, MeasureSpec.AT_MOST);
+                mAppName.measure(childWidthSpec, wrapContentHeightSpec);
+                overFlow -= appWidth - newSize;
+            }
+            if (overFlow > 0 && mSubTextView.getVisibility() != GONE) {
+                // we're still too big
+                final int subTextWidth = mSubTextView.getMeasuredWidth();
+                int newSize = Math.max(0, subTextWidth - overFlow);
+                int childWidthSpec = MeasureSpec.makeMeasureSpec(newSize, MeasureSpec.AT_MOST);
+                mSubTextView.measure(childWidthSpec, wrapContentHeightSpec);
+            }
+            totalWidth = givenWidth;
+        }
+        setMeasuredDimension(totalWidth, givenHeight);
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int l, int t, int r, int b) {
+        super.onLayout(changed, l, t, r, b);
+        updateTouchListener();
+    }
+
+    private void updateTouchListener() {
+        if (mExpandClickListener != null) {
+            mTouchListener.bindTouchRects();
+        }
+    }
+
+    @Override
+    public void setOnClickListener(@Nullable OnClickListener l) {
+        mExpandClickListener = l;
+        setOnTouchListener(mExpandClickListener != null ? mTouchListener : null);
+        updateTouchListener();
+    }
+
+    public void setChildCount(int childCount) {
+        if (childCount > 0) {
+            mChildCount.setText(getContext().getString(
+                    com.android.internal.R.string.notification_children_count_bracketed,
+                    childCount));
+            mChildCount.setVisibility(VISIBLE);
+        } else {
+            mChildCount.setVisibility(GONE);
+        }
+    }
+
+    public class HeaderTouchListener implements View.OnTouchListener {
+
+        private final ArrayList<Rect> mTouchRects = new ArrayList<>();
+        private int mTouchSlop;
+        private boolean mTrackGesture;
+        private float mDownX;
+        private float mDownY;
+
+        public HeaderTouchListener() {
+        }
+
+        public void bindTouchRects() {
+            mTouchRects.clear();
+            addRectAroundViewView(mIcon);
+            addRectAroundViewView(mExpandButton);
+            addInBetweenRect();
+            mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
+        }
+
+        private void addInBetweenRect() {
+            final Rect r = new Rect();
+            r.top = 0;
+            r.bottom = (int) (32 * getResources().getDisplayMetrics().density);
+            Rect leftRect = mTouchRects.get(0);
+            r.left = leftRect.right;
+            Rect rightRect = mTouchRects.get(1);
+            r.right = rightRect.left;
+            mTouchRects.add(r);
+        }
+
+        private void addRectAroundViewView(View view) {
+            final Rect r = getRectAroundView(view);
+            mTouchRects.add(r);
+        }
+
+        private Rect getRectAroundView(View view) {
+            float size = 48 * getResources().getDisplayMetrics().density;
+            final Rect r = new Rect();
+            if (view.getVisibility() == GONE) {
+                view = getFirstChildNotGone();
+                r.left = (int) (view.getLeft() - size / 2.0f);
+            } else {
+                r.left = (int) ((view.getLeft() + view.getRight()) / 2.0f - size / 2.0f);
+            }
+            r.top = (int) ((view.getTop() + view.getBottom()) / 2.0f - size / 2.0f);
+            r.bottom = (int) (r.top + size);
+            r.right = (int) (r.left + size);
+            return r;
+        }
+
+        @Override
+        public boolean onTouch(View v, MotionEvent event) {
+            float x = event.getX();
+            float y = event.getY();
+            switch (event.getActionMasked() & MotionEvent.ACTION_MASK) {
+                case MotionEvent.ACTION_DOWN:
+                    mTrackGesture = false;
+                    if (isInside(x, y)) {
+                        mTrackGesture = true;
+                        return true;
+                    }
+                    break;
+                case MotionEvent.ACTION_MOVE:
+                    if (mTrackGesture) {
+                        if (Math.abs(mDownX - x) > mTouchSlop
+                                || Math.abs(mDownY - y) > mTouchSlop) {
+                            mTrackGesture = false;
+                        }
+                    }
+                    break;
+                case MotionEvent.ACTION_UP:
+                    if (mTrackGesture) {
+                        mExpandClickListener.onClick(NotificationHeaderView.this);
+                    }
+                    break;
+            }
+            return mTrackGesture;
+        }
+
+        private boolean isInside(float x, float y) {
+            for (int i = 0; i < mTouchRects.size(); i++) {
+                Rect r = mTouchRects.get(i);
+                if (r.contains((int) x, (int) y)) {
+                    mDownX = x;
+                    mDownY = y;
+                    return true;
+                }
+            }
+            return false;
+        }
+    }
+
+    private View getFirstChildNotGone() {
+        for (int i = 0; i < getChildCount(); i++) {
+            final View child = getChildAt(i);
+            if (child.getVisibility() != GONE) {
+                return child;
+            }
+        }
+        return this;
+    }
+}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 692fa66..da66f97 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -19429,6 +19429,7 @@
      *
      * @attr ref android.R.styleable#View_minHeight
      */
+    @RemotableViewMethod
     public void setMinimumHeight(int minHeight) {
         mMinHeight = minHeight;
         requestLayout();
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 ce1c108..0dd803a2 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -1638,6 +1638,48 @@
     }
 
     /**
+     * Helper action to set layout margin on a View.
+     */
+    private class ViewMarginEndAction extends Action {
+        public ViewMarginEndAction(int viewId, int end) {
+            this.viewId = viewId;
+            this.end = end;
+        }
+
+        public ViewMarginEndAction(Parcel parcel) {
+            viewId = parcel.readInt();
+            end = parcel.readInt();
+        }
+
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeInt(TAG);
+            dest.writeInt(viewId);
+            dest.writeInt(end);
+        }
+
+        @Override
+        public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
+            final View target = root.findViewById(viewId);
+            if (target == null) {
+                return;
+            }
+            ViewGroup.LayoutParams layoutParams = target.getLayoutParams();
+            if (layoutParams instanceof ViewGroup.MarginLayoutParams) {
+                ((ViewGroup.MarginLayoutParams) layoutParams).setMarginEnd(end);
+                target.setLayoutParams(layoutParams);
+            }
+        }
+
+        public String getActionName() {
+            return "ViewMarginEndAction";
+        }
+
+        int end;
+
+        public final static int TAG = 19;
+    }
+
+    /**
      * Helper action to set a color filter on a compound drawable on a TextView. Supports relative
      * (s/t/e/b) or cardinal (l/t/r/b) arrangement.
      */
@@ -1942,6 +1984,9 @@
                         case SetRemoteInputsAction.TAG:
                             mActions.add(new SetRemoteInputsAction(parcel));
                             break;
+                        case ViewMarginEndAction.TAG:
+                            mActions.add(new ViewMarginEndAction(parcel));
+                            break;
                         default:
                             throw new ActionException("Tag " + tag + " not found");
                     }
@@ -2549,6 +2594,19 @@
     }
 
     /**
+     * @hide
+     * Equivalent to calling {@link android.view.ViewGroup.MarginLayoutParams#setMarginEnd(int)}.
+     * Only works if the {@link View#getLayoutParams()} supports margins.
+     * Hidden for now since we don't want to support this for all different layout margins yet.
+     *
+     * @param viewId The id of the view to change
+     * @param endMargin the left padding in pixels
+     */
+    public void setViewLayoutMarginEnd(int viewId, int endMargin) {
+        addAction(new ViewMarginEndAction(viewId, endMargin));
+    }
+
+    /**
      * Call a method taking one boolean on a view in the layout for this RemoteViews.
      *
      * @param viewId The id of the view on which to call the method.
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 2f385c1..d666939 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -6828,7 +6828,10 @@
         if (mEditor != null) mEditor.prepareCursorControllers();
     }
 
-    private Layout makeSingleLayout(int wantWidth, BoringLayout.Metrics boring, int ellipsisWidth,
+    /**
+     * @hide
+     */
+    protected Layout makeSingleLayout(int wantWidth, BoringLayout.Metrics boring, int ellipsisWidth,
             Layout.Alignment alignment, boolean shouldEllipsize, TruncateAt effectiveEllipsize,
             boolean useSaved) {
         Layout result = null;
@@ -9708,7 +9711,10 @@
         }
     }
 
-    TextDirectionHeuristic getTextDirectionHeuristic() {
+    /**
+     * @hide
+     */
+    protected TextDirectionHeuristic getTextDirectionHeuristic() {
         if (hasPasswordTransformationMethod()) {
             // passwords fields should be LTR
             return TextDirectionHeuristics.LTR;
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/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/java/com/android/internal/widget/ImageFloatingTextView.java b/core/java/com/android/internal/widget/ImageFloatingTextView.java
new file mode 100644
index 0000000..c4ed2e1
--- /dev/null
+++ b/core/java/com/android/internal/widget/ImageFloatingTextView.java
@@ -0,0 +1,89 @@
+/*
+ * 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.internal.widget;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.text.BoringLayout;
+import android.text.Layout;
+import android.text.StaticLayout;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.view.RemotableViewMethod;
+import android.widget.RemoteViews;
+import android.widget.TextView;
+
+/**
+ * A TextView that can float around an image on the end.
+ *
+ * @hide
+ */
+@RemoteViews.RemoteView
+public class ImageFloatingTextView extends TextView {
+
+    private boolean mHasImage;
+
+    public ImageFloatingTextView(Context context) {
+        this(context, null);
+    }
+
+    public ImageFloatingTextView(Context context, @Nullable AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public ImageFloatingTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public ImageFloatingTextView(Context context, AttributeSet attrs, int defStyleAttr,
+            int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    @Override
+    protected Layout makeSingleLayout(int wantWidth, BoringLayout.Metrics boring, int ellipsisWidth,
+            Layout.Alignment alignment, boolean shouldEllipsize,
+            TextUtils.TruncateAt effectiveEllipsize, boolean useSaved) {
+        CharSequence text = getText() == null ? "" : getText();
+        StaticLayout.Builder builder = StaticLayout.Builder.obtain(text, 0, text.length(),
+                getPaint(), wantWidth)
+                .setAlignment(alignment)
+                .setTextDirection(getTextDirectionHeuristic())
+                .setLineSpacing(getLineSpacingExtra(), getLineSpacingMultiplier())
+                .setIncludePad(getIncludeFontPadding())
+                .setBreakStrategy(Layout.BREAK_STRATEGY_HIGH_QUALITY)
+                .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_FULL);
+        // we set the endmargin on the first 2 lines. this works just in our case but that's
+        // sufficient for now.
+        int endMargin = (int) (getResources().getDisplayMetrics().density * 52);
+        int[] margins = mHasImage ? new int[] {endMargin, endMargin, 0} : null;
+        if (getLayoutDirection() == LAYOUT_DIRECTION_RTL) {
+            builder.setIndents(margins, null);
+        } else {
+            builder.setIndents(null, margins);
+        }
+
+        return builder.build();
+    }
+
+    @RemotableViewMethod
+    public void setHasImage(boolean hasImage) {
+        mHasImage = hasImage;
+        // The new layout will be automatically created when the text is
+        // set again by the notification.
+    }
+}
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 4d648ce..0473016f 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -93,6 +93,7 @@
     android_util_Process.cpp \
     android_util_StringBlock.cpp \
     android_util_XmlBlock.cpp \
+    android_util_jar_StrictJarFile.cpp \
     android_graphics_Canvas.cpp \
     android_graphics_Picture.cpp \
     android/graphics/AutoDecodeCancel.cpp \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index bd41c5d..f6f45b5 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -176,6 +176,7 @@
 extern int register_android_app_ActivityThread(JNIEnv *env);
 extern int register_android_app_NativeActivity(JNIEnv *env);
 extern int register_android_media_RemoteDisplay(JNIEnv *env);
+extern int register_android_util_jar_StrictJarFile(JNIEnv* env);
 extern int register_android_view_InputChannel(JNIEnv* env);
 extern int register_android_view_InputDevice(JNIEnv* env);
 extern int register_android_view_InputEventReceiver(JNIEnv* env);
@@ -1359,6 +1360,7 @@
     REG_JNI(register_android_app_backup_FullBackup),
     REG_JNI(register_android_app_ActivityThread),
     REG_JNI(register_android_app_NativeActivity),
+    REG_JNI(register_android_util_jar_StrictJarFile),
     REG_JNI(register_android_view_InputChannel),
     REG_JNI(register_android_view_InputEventReceiver),
     REG_JNI(register_android_view_InputEventSender),
@@ -1374,6 +1376,8 @@
     REG_JNI(register_android_animation_PropertyValuesHolder),
     REG_JNI(register_com_android_internal_content_NativeLibraryHelper),
     REG_JNI(register_com_android_internal_net_NetworkStatsFactory),
+
+
 };
 
 /*
diff --git a/core/jni/android_util_jar_StrictJarFile.cpp b/core/jni/android_util_jar_StrictJarFile.cpp
new file mode 100644
index 0000000..7f8f708
--- /dev/null
+++ b/core/jni/android_util_jar_StrictJarFile.cpp
@@ -0,0 +1,172 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+#define LOG_TAG "StrictJarFile"
+
+#include <memory>
+#include <string>
+
+#include "JNIHelp.h"
+#include "JniConstants.h"
+#include "ScopedLocalRef.h"
+#include "ScopedUtfChars.h"
+#include "jni.h"
+#include "ziparchive/zip_archive.h"
+#include "cutils/log.h"
+
+namespace android {
+
+// The method ID for ZipEntry.<init>(String,String,JJJIII[BJJ)
+static jmethodID zipEntryCtor;
+
+static void throwIoException(JNIEnv* env, const int32_t errorCode) {
+  jniThrowException(env, "java/io/IOException", ErrorCodeString(errorCode));
+}
+
+static jobject newZipEntry(JNIEnv* env, const ZipEntry& entry, jstring entryName) {
+  return env->NewObject(JniConstants::zipEntryClass,
+                        zipEntryCtor,
+                        entryName,
+                        NULL,  // comment
+                        static_cast<jlong>(entry.crc32),
+                        static_cast<jlong>(entry.compressed_length),
+                        static_cast<jlong>(entry.uncompressed_length),
+                        static_cast<jint>(entry.method),
+                        static_cast<jint>(0),  // time
+                        NULL,  // byte[] extra
+                        static_cast<jlong>(entry.offset));
+}
+
+static jlong StrictJarFile_nativeOpenJarFile(JNIEnv* env, jobject, jstring fileName) {
+  ScopedUtfChars fileChars(env, fileName);
+  if (fileChars.c_str() == NULL) {
+    return static_cast<jlong>(-1);
+  }
+
+  ZipArchiveHandle handle;
+  int32_t error = OpenArchive(fileChars.c_str(), &handle);
+  if (error) {
+    CloseArchive(handle);
+    throwIoException(env, error);
+    return static_cast<jlong>(-1);
+  }
+
+  return reinterpret_cast<jlong>(handle);
+}
+
+class IterationHandle {
+ public:
+  IterationHandle() :
+    cookie_(NULL) {
+  }
+
+  void** CookieAddress() {
+    return &cookie_;
+  }
+
+  ~IterationHandle() {
+    EndIteration(cookie_);
+  }
+
+ private:
+  void* cookie_;
+};
+
+
+static jlong StrictJarFile_nativeStartIteration(JNIEnv* env, jobject, jlong nativeHandle,
+                                                jstring prefix) {
+  ScopedUtfChars prefixChars(env, prefix);
+  if (prefixChars.c_str() == NULL) {
+    return static_cast<jlong>(-1);
+  }
+
+  IterationHandle* handle = new IterationHandle();
+  int32_t error = 0;
+  if (prefixChars.size() == 0) {
+    error = StartIteration(reinterpret_cast<ZipArchiveHandle>(nativeHandle),
+                           handle->CookieAddress(), NULL, NULL);
+  } else {
+    ZipString entry_name(prefixChars.c_str());
+    error = StartIteration(reinterpret_cast<ZipArchiveHandle>(nativeHandle),
+                           handle->CookieAddress(), &entry_name, NULL);
+  }
+
+  if (error) {
+    throwIoException(env, error);
+    return static_cast<jlong>(-1);
+  }
+
+  return reinterpret_cast<jlong>(handle);
+}
+
+static jobject StrictJarFile_nativeNextEntry(JNIEnv* env, jobject, jlong iterationHandle) {
+  ZipEntry data;
+  ZipString entryName;
+
+  IterationHandle* handle = reinterpret_cast<IterationHandle*>(iterationHandle);
+  const int32_t error = Next(*handle->CookieAddress(), &data, &entryName);
+  if (error) {
+    delete handle;
+    return NULL;
+  }
+
+  std::unique_ptr<char[]> entryNameCString(new char[entryName.name_length + 1]);
+  memcpy(entryNameCString.get(), entryName.name, entryName.name_length);
+  entryNameCString[entryName.name_length] = '\0';
+  ScopedLocalRef<jstring> entryNameString(env, env->NewStringUTF(entryNameCString.get()));
+
+  return newZipEntry(env, data, entryNameString.get());
+}
+
+static jobject StrictJarFile_nativeFindEntry(JNIEnv* env, jobject, jlong nativeHandle,
+                                             jstring entryName) {
+  ScopedUtfChars entryNameChars(env, entryName);
+  if (entryNameChars.c_str() == NULL) {
+    return NULL;
+  }
+
+  ZipEntry data;
+  const int32_t error = FindEntry(reinterpret_cast<ZipArchiveHandle>(nativeHandle),
+                                  ZipString(entryNameChars.c_str()), &data);
+  if (error) {
+    return NULL;
+  }
+
+  return newZipEntry(env, data, entryName);
+}
+
+static void StrictJarFile_nativeClose(JNIEnv*, jobject, jlong nativeHandle) {
+  CloseArchive(reinterpret_cast<ZipArchiveHandle>(nativeHandle));
+}
+
+static JNINativeMethod gMethods[] = {
+  NATIVE_METHOD(StrictJarFile, nativeOpenJarFile, "(Ljava/lang/String;)J"),
+  NATIVE_METHOD(StrictJarFile, nativeStartIteration, "(JLjava/lang/String;)J"),
+  NATIVE_METHOD(StrictJarFile, nativeNextEntry, "(J)Ljava/util/zip/ZipEntry;"),
+  NATIVE_METHOD(StrictJarFile, nativeFindEntry, "(JLjava/lang/String;)Ljava/util/zip/ZipEntry;"),
+  NATIVE_METHOD(StrictJarFile, nativeClose, "(J)V"),
+};
+
+void register_android_util_jar_StrictJarFile(JNIEnv* env) {
+  jniRegisterNativeMethods(env, "android/util/jar/StrictJarFile", gMethods, NELEM(gMethods));
+
+  zipEntryCtor = env->GetMethodID(JniConstants::zipEntryClass, "<init>",
+      "(Ljava/lang/String;Ljava/lang/String;JJJII[BJ)V");
+  LOG_ALWAYS_FATAL_IF(zipEntryCtor == NULL, "Unable to find ZipEntry.<init>");
+}
+
+}; // namespace android
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/drawable/ic_arrow_drop_down.xml b/core/res/res/drawable/ic_arrow_drop_down.xml
new file mode 100644
index 0000000..c8bb411
--- /dev/null
+++ b/core/res/res/drawable/ic_arrow_drop_down.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="14.0dp"
+    android:height="14.0dp"
+    android:viewportWidth="24.0"
+    android:viewportHeight="24.0">
+    <path
+        android:pathData="M16.600000,8.600000l-4.600000,4.599999 -4.600000,-4.599999 -1.400000,1.400000 6.000000,6.000000 6.000000,-6.000000z"
+        android:fillColor="#FF000000"/>
+</vector>
diff --git a/core/res/res/drawable/ic_arrow_up_14dp.xml b/core/res/res/drawable/ic_arrow_up_14dp.xml
new file mode 100644
index 0000000..c4cc0d1
--- /dev/null
+++ b/core/res/res/drawable/ic_arrow_up_14dp.xml
@@ -0,0 +1,24 @@
+<!--
+  ~ 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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="14.0dp"
+        android:height="14.0dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:pathData="M12.000000,8.000000l-6.000000,6.000000 1.400000,1.400000 4.600000,-4.599999 4.600000,4.599999 1.400000,-1.400000z"
+        android:fillColor="#FF000000"/>
+</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/layout/notification_material_action.xml b/core/res/res/layout/notification_material_action.xml
index f4bc918..62602d8 100644
--- a/core/res/res/layout/notification_material_action.xml
+++ b/core/res/res/layout/notification_material_action.xml
@@ -21,7 +21,7 @@
     android:layout_width="wrap_content"
     android:layout_height="48dp"
     android:layout_gravity="center"
-    android:layout_marginStart="8dp"
+    android:layout_marginStart="4dp"
     android:textColor="@color/secondary_text_material_light"
     android:singleLine="true"
     android:ellipsize="end"
diff --git a/core/res/res/layout/notification_material_action_list.xml b/core/res/res/layout/notification_material_action_list.xml
index edaf020..2a89faa 100644
--- a/core/res/res/layout/notification_material_action_list.xml
+++ b/core/res/res/layout/notification_material_action_list.xml
@@ -17,13 +17,14 @@
 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
         android:id="@+id/actions_container"
         android:layout_width="match_parent"
-        android:layout_height="wrap_content">
-
+        android:layout_height="wrap_content"
+        android:layout_marginStart="-16dp"
+        android:layout_marginEnd="-16dp">
     <LinearLayout
             android:id="@+id/actions"
             android:layout_width="match_parent"
             android:layout_height="56dp"
-            android:paddingEnd="8dp"
+            android:paddingEnd="4dp"
             android:orientation="horizontal"
             android:visibility="gone"
             android:background="#ffeeeeee"
diff --git a/core/res/res/layout/notification_material_media_action.xml b/core/res/res/layout/notification_material_media_action.xml
index 1d52e54..19a6f84 100644
--- a/core/res/res/layout/notification_material_media_action.xml
+++ b/core/res/res/layout/notification_material_media_action.xml
@@ -19,10 +19,12 @@
     style="@android:style/Widget.Material.Button.Borderless.Small"
     android:id="@+id/action0"
     android:layout_width="48dp"
-    android:layout_height="match_parent"
-    android:layout_marginLeft="2dp"
-    android:layout_marginRight="2dp"
-    android:layout_weight="1"
+    android:layout_height="48dp"
+    android:paddingBottom="8dp"
+    android:paddingTop="8dp"
+    android:paddingStart="8dp"
+    android:paddingEnd="8dp"
+    android:layout_marginEnd="2dp"
     android:gravity="center"
     android:background="@drawable/notification_material_media_action_background"
     />
diff --git a/core/res/res/layout/notification_template_header.xml b/core/res/res/layout/notification_template_header.xml
new file mode 100644
index 0000000..aceae9f
--- /dev/null
+++ b/core/res/res/layout/notification_template_header.xml
@@ -0,0 +1,132 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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
+  -->
+
+<NotificationHeaderView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/notification_header"
+    android:orientation="horizontal"
+    android:layout_width="wrap_content"
+    android:layout_height="48dp"
+    android:clipChildren="false"
+    android:layout_gravity="start|top"
+    android:gravity="center_vertical"
+    android:paddingTop="5dp"
+    android:paddingBottom="16dp"
+    android:paddingStart="@dimen/notification_content_margin_start"
+    android:paddingEnd="16dp">
+    <ImageView
+        android:id="@+id/icon"
+        android:layout_width="18dp"
+        android:layout_height="18dp"
+        android:layout_marginEnd="3dp"
+        />
+    <TextView
+        android:id="@+id/number_of_children"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textAppearance="@style/TextAppearance.Material.Notification"
+        android:layout_marginEnd="3dp"
+        android:layout_marginStart="2dp"
+        android:visibility="gone"
+        android:singleLine="true"
+        />
+    <TextView
+        android:id="@+id/app_name_text"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textAppearance="@style/TextAppearance.Material.Notification.Info"
+        android:layout_marginStart="3dp"
+        android:layout_marginEnd="2dp"
+        android:singleLine="true"
+        />
+    <TextView
+        android:id="@+id/sub_text_divider"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textAppearance="@style/TextAppearance.Material.Notification.Info"
+        android:layout_marginStart="2dp"
+        android:layout_marginEnd="2dp"
+        android:text="@string/notification_header_divider_symbol"
+        android:visibility="gone"/>
+    <TextView
+        android:id="@+id/header_sub_text"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textAppearance="@style/TextAppearance.Material.Notification.Info"
+        android:layout_marginStart="2dp"
+        android:layout_marginEnd="2dp"
+        android:visibility="gone"
+        android:singleLine="true"/>
+    <TextView
+        android:id="@+id/content_info_divider"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textAppearance="@style/TextAppearance.Material.Notification.Info"
+        android:layout_marginStart="2dp"
+        android:layout_marginEnd="2dp"
+        android:text="@string/notification_header_divider_symbol"
+        android:singleLine="true"
+        android:visibility="gone"/>
+    <TextView
+        android:id="@+id/header_content_info"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textAppearance="@style/TextAppearance.Material.Notification.Info"
+        android:layout_marginStart="2dp"
+        android:layout_marginEnd="2dp"
+        android:visibility="gone"
+        android:maxWidth="72dp"
+        android:singleLine="true"/>
+    <TextView
+        android:id="@+id/time_divider"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textAppearance="@style/TextAppearance.Material.Notification.Info"
+        android:layout_marginStart="2dp"
+        android:layout_marginEnd="2dp"
+        android:text="@string/notification_header_divider_symbol"
+        android:singleLine="true"
+        android:visibility="gone"/>
+    <ViewStub
+        android:id="@+id/time"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="2dp"
+        android:layout_marginEnd="2dp"
+        android:layout="@layout/notification_template_part_time"
+        android:visibility="gone"
+        />
+    <ViewStub
+        android:id="@+id/chronometer"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="2dp"
+        android:layout_marginEnd="2dp"
+        android:layout="@layout/notification_template_part_chronometer"
+        android:visibility="gone"
+        />
+    <ImageView
+        android:id="@+id/expand_button"
+        android:background="@null"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:paddingTop="1dp"
+        android:src="@drawable/ic_arrow_drop_down"
+        android:visibility="gone"
+        />
+</NotificationHeaderView>
+
diff --git a/core/res/res/layout/notification_template_icon_group.xml b/core/res/res/layout/notification_template_icon_group.xml
deleted file mode 100644
index fa66163..0000000
--- a/core/res/res/layout/notification_template_icon_group.xml
+++ /dev/null
@@ -1,45 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ 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
-  -->
-
-<FrameLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:internal="http://schemas.android.com/apk/prv/res/android"
-    android:layout_width="@dimen/notification_large_icon_width"
-    android:layout_height="@dimen/notification_large_icon_height"
-    android:id="@+id/icon_group"
-    >
-    <ImageView android:id="@+id/icon"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:layout_marginTop="12dp"
-        android:layout_marginBottom="12dp"
-        android:layout_marginStart="12dp"
-        android:layout_marginEnd="12dp"
-        android:scaleType="centerInside"
-        />
-    <ImageView android:id="@+id/right_icon"
-        android:layout_width="16dp"
-        android:layout_height="16dp"
-        android:padding="3dp"
-        android:layout_gravity="end|bottom"
-        android:scaleType="centerInside"
-        android:visibility="gone"
-        android:layout_marginEnd="8dp"
-        android:layout_marginBottom="8dp"
-        />
-</FrameLayout>
-
diff --git a/core/res/res/layout/notification_template_material_base.xml b/core/res/res/layout/notification_template_material_base.xml
index 94bbec8..b69eb24 100644
--- a/core/res/res/layout/notification_template_material_base.xml
+++ b/core/res/res/layout/notification_template_material_base.xml
@@ -18,24 +18,32 @@
 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/status_bar_latest_event_content"
     android:layout_width="match_parent"
-    android:layout_height="64dp"
+    android:layout_height="wrap_content"
     android:tag="base"
     >
-    <include layout="@layout/notification_template_icon_group"
-        android:layout_width="@dimen/notification_large_icon_width"
-        android:layout_height="@dimen/notification_large_icon_height"
-        />
+    <include layout="@layout/notification_template_header" />
     <LinearLayout
         android:id="@+id/notification_main_column"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:layout_gravity="top"
-        android:layout_marginStart="@dimen/notification_large_icon_width"
-        android:minHeight="@dimen/notification_large_icon_height"
+        android:layout_marginStart="@dimen/notification_content_margin_start"
+        android:layout_marginEnd="@dimen/notification_content_margin_end"
+        android:layout_marginTop="@dimen/notification_content_margin_top"
+        android:minHeight="@dimen/notification_min_content_height"
         android:orientation="vertical"
         >
         <include layout="@layout/notification_template_part_line1" />
-        <include layout="@layout/notification_template_part_line2" />
         <include layout="@layout/notification_template_part_line3" />
     </LinearLayout>
+    <FrameLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_gravity="bottom"
+        android:layout_marginStart="@dimen/notification_content_margin_start"
+        android:layout_marginBottom="11dp"
+        android:layout_marginEnd="@dimen/notification_content_margin_end">
+        <include layout="@layout/notification_template_progress" />
+    </FrameLayout>
+    <include layout="@layout/notification_template_right_icon" />
 </FrameLayout>
diff --git a/core/res/res/layout/notification_template_material_big_base.xml b/core/res/res/layout/notification_template_material_big_base.xml
index 97df978e..8c78b8d 100644
--- a/core/res/res/layout/notification_template_material_big_base.xml
+++ b/core/res/res/layout/notification_template_material_big_base.xml
@@ -14,71 +14,56 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License
   -->
-
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/status_bar_latest_event_content"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
+    android:orientation="vertical"
     android:tag="big"
     >
-    <include layout="@layout/notification_template_icon_group"
-        android:layout_width="@dimen/notification_large_icon_width"
-        android:layout_height="@dimen/notification_large_icon_height"
-        />
-    <LinearLayout
-        android:id="@+id/notification_main_column"
+    <FrameLayout
+        android:id="@+id/status_bar_latest_event_content"
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/notification_min_height"
+        android:layout_gravity="top"
+        android:tag="base"
+        >
+        <include layout="@layout/notification_template_header" />
+        <LinearLayout
+            android:id="@+id/notification_main_column"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:layout_gravity="top"
+            android:layout_marginStart="@dimen/notification_content_margin_start"
+            android:layout_marginEnd="@dimen/notification_content_margin_end"
+            android:layout_marginTop="@dimen/notification_content_margin_top"
+            android:minHeight="@dimen/notification_min_content_height"
+            android:orientation="vertical"
+            >
+            <include layout="@layout/notification_template_part_line1" />
+            <include layout="@layout/notification_template_part_line3" />
+        </LinearLayout>
+        <FrameLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_gravity="bottom"
+            android:layout_marginStart="@dimen/notification_content_margin_start"
+            android:layout_marginBottom="11dp"
+            android:layout_marginEnd="@dimen/notification_content_margin_end">
+            <include layout="@layout/notification_template_progress" />
+        </FrameLayout>
+        <include layout="@layout/notification_template_right_icon" />
+    </FrameLayout>
+    <ImageView
+        android:layout_width="match_parent"
+        android:layout_height="1dp"
+        android:id="@+id/action_divider"
+        android:visibility="gone"
+        android:background="@drawable/notification_template_divider" />
+    <include
+        layout="@layout/notification_material_action_list"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:layout_gravity="top"
-        android:layout_marginStart="@dimen/notification_large_icon_width"
-        android:minHeight="@dimen/notification_large_icon_height"
-        android:orientation="vertical"
-        >
-        <include layout="@layout/notification_template_part_line1" />
-        <include layout="@layout/notification_template_part_line2" />
-        <LinearLayout
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_marginEnd="8dp"
-            android:orientation="horizontal"
-            android:gravity="top"
-            >
-            <TextView android:id="@+id/big_text"
-                android:textAppearance="@style/TextAppearance.Material.Notification"
-                android:layout_width="0dp"
-                android:layout_height="wrap_content"
-                android:layout_weight="1"
-                android:singleLine="false"
-                android:visibility="gone"
-                />
-            <ImageView android:id="@+id/profile_badge_large_template"
-                android:layout_width="@dimen/notification_badge_size"
-                android:layout_height="@dimen/notification_badge_size"
-                android:layout_weight="0"
-                android:layout_marginStart="4dp"
-                android:scaleType="fitCenter"
-                android:visibility="gone"
-                android:contentDescription="@string/notification_work_profile_content_description"
-                />
-        </LinearLayout>
-        <include
-            layout="@layout/notification_template_part_line3"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_marginEnd="8dp"
-            />
-        <ImageView
-            android:layout_width="match_parent"
-            android:layout_height="1dp"
-            android:layout_marginTop="10dp"
-            android:id="@+id/action_divider"
-            android:visibility="gone"
-            android:background="@drawable/notification_template_divider" />
-        <include
-            layout="@layout/notification_material_action_list"
-            android:layout_marginStart="-8dp"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            />
-    </LinearLayout>
-</FrameLayout>
+        />
+</LinearLayout>
diff --git a/core/res/res/layout/notification_template_material_big_media.xml b/core/res/res/layout/notification_template_material_big_media.xml
index 7fd93de..0427c8a 100644
--- a/core/res/res/layout/notification_template_material_big_media.xml
+++ b/core/res/res/layout/notification_template_material_big_media.xml
@@ -15,44 +15,53 @@
   ~ limitations under the License
   -->
 
+<!-- Layout for the expanded media notification -->
 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/status_bar_latest_event_content"
     android:layout_width="match_parent"
     android:layout_height="128dp"
     android:background="#00000000"
-    android:tag="bigMedia"
+    android:tag="bigMediaNarrow"
     >
-    <include layout="@layout/notification_template_icon_group"
-        android:layout_width="@dimen/notification_large_icon_width"
-        android:layout_height="@dimen/notification_large_icon_height"
-        />
+    <include layout="@layout/notification_template_header"
+        android:layout_width="fill_parent"
+        android:layout_height="48dp"
+        android:layout_marginEnd="106dp"/>
     <LinearLayout
+        android:id="@+id/notification_main_column"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:layout_marginStart="@dimen/notification_large_icon_width"
-        android:minHeight="@dimen/notification_large_icon_height"
+        android:layout_marginTop="@dimen/notification_content_margin_top"
+        android:layout_marginStart="@dimen/notification_content_margin_start"
+        android:layout_marginEnd="24dp"
+        android:layout_toStartOf="@id/right_icon"
+        android:minHeight="@dimen/notification_min_content_height"
         android:orientation="vertical"
         >
         <include layout="@layout/notification_template_part_line1" />
-        <include layout="@layout/notification_template_part_line2" />
         <include layout="@layout/notification_template_part_line3" />
     </LinearLayout>
     <LinearLayout
         android:id="@+id/media_actions"
-        android:layout_width="match_parent"
-        android:layout_height="48dp"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
         android:layout_alignParentBottom="true"
-        android:layout_marginStart="12dp"
-        android:layout_marginEnd="12dp"
+        android:layout_alignParentStart="true"
+        android:paddingStart="8dp"
+        android:paddingBottom="8dp"
         android:orientation="horizontal"
         android:layoutDirection="ltr"
         >
         <!-- media buttons will be added here -->
     </LinearLayout>
-    <ImageView
-        android:layout_width="match_parent"
-        android:layout_height="1dp"
-        android:layout_above="@id/media_actions"
-        android:id="@+id/action_divider"
-        android:background="@drawable/notification_template_divider_media" />
+
+    <ImageView android:id="@+id/right_icon"
+        android:layout_width="96dp"
+        android:layout_height="96dp"
+        android:layout_marginEnd="16dp"
+        android:layout_marginTop="16dp"
+        android:layout_alignParentEnd="true"
+        android:layout_alignParentTop="true"
+        android:scaleType="centerCrop"
+        />
 </RelativeLayout>
diff --git a/core/res/res/layout/notification_template_material_big_media_narrow.xml b/core/res/res/layout/notification_template_material_big_media_narrow.xml
deleted file mode 100644
index 807cfaf..0000000
--- a/core/res/res/layout/notification_template_material_big_media_narrow.xml
+++ /dev/null
@@ -1,63 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ 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
-  -->
-
-<!-- Layout to be used with only max 3 actions. It has a much larger picture at the left side-->
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/status_bar_latest_event_content"
-    android:layout_width="match_parent"
-    android:layout_height="128dp"
-    android:background="#00000000"
-    android:tag="bigMediaNarrow"
-    >
-    <ImageView android:id="@+id/icon"
-        android:layout_width="128dp"
-        android:layout_height="128dp"
-        android:scaleType="centerCrop"
-        />
-    <LinearLayout
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_marginStart="12dp"
-        android:layout_toEndOf="@id/icon"
-        android:minHeight="@dimen/notification_large_icon_height"
-        android:orientation="vertical"
-        >
-        <include layout="@layout/notification_template_part_line1" />
-        <include layout="@layout/notification_template_part_line2" />
-        <include layout="@layout/notification_template_part_line3" />
-    </LinearLayout>
-    <LinearLayout
-        android:id="@+id/media_actions"
-        android:layout_width="match_parent"
-        android:layout_height="48dp"
-        android:layout_toEndOf="@id/icon"
-        android:layout_alignParentBottom="true"
-        android:layout_marginStart="12dp"
-        android:layout_marginEnd="12dp"
-        android:orientation="horizontal"
-        android:layoutDirection="ltr"
-        >
-        <!-- media buttons will be added here -->
-    </LinearLayout>
-    <ImageView
-        android:layout_width="match_parent"
-        android:layout_height="1dp"
-        android:layout_toEndOf="@id/icon"
-        android:layout_above="@id/media_actions"
-        android:id="@+id/action_divider"
-        android:background="@drawable/notification_template_divider_media" />
-</RelativeLayout>
diff --git a/core/res/res/layout/notification_template_material_big_picture.xml b/core/res/res/layout/notification_template_material_big_picture.xml
index f3768b5..74e7775 100644
--- a/core/res/res/layout/notification_template_material_big_picture.xml
+++ b/core/res/res/layout/notification_template_material_big_picture.xml
@@ -21,39 +21,45 @@
     android:layout_height="match_parent"
     android:tag="bigPicture"
     >
-    <ImageView
-        android:id="@+id/big_picture"
-        android:layout_width="match_parent"
-        android:layout_height="192dp"
-        android:layout_marginTop="64dp"
-        android:layout_gravity="bottom"
-        android:scaleType="centerCrop"
-        />
-    <ImageView
-        android:layout_width="match_parent"
-        android:layout_height="6dp"
-        android:layout_marginTop="64dp"
-        android:scaleType="fitXY"
-        android:src="@drawable/title_bar_shadow"
-        />
-    <include layout="@layout/notification_template_material_base"
-        android:layout_width="match_parent"
-        android:layout_height="64dp"
-        />
-  <FrameLayout
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_marginTop="208dp"
-        android:paddingStart="@dimen/notification_large_icon_width"
-        android:layout_gravity="bottom"
-        android:background="#CCEEEEEE"
-        >
-        <include
-            layout="@layout/notification_material_action_list"
-            android:id="@+id/actions"
-            android:layout_gravity="bottom"
+    <include layout="@layout/notification_template_header" />
+    <include layout="@layout/notification_template_right_icon" />
+    <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:layout_gravity="top"
+            android:paddingStart="@dimen/notification_content_margin_start"
+            android:paddingEnd="@dimen/notification_content_margin_end"
+            android:layout_marginTop="@dimen/notification_content_margin_top"
+            android:clipToPadding="false"
+            android:orientation="vertical"
+            >
+        <LinearLayout
+            android:id="@+id/notification_main_column"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            />
-    </FrameLayout>
+            android:orientation="vertical">
+            <include layout="@layout/notification_template_part_line1"/>
+            <include layout="@layout/notification_template_progress"/>
+            <include layout="@layout/notification_template_part_line3"/>
+        </LinearLayout>
+        <ImageView
+                android:id="@+id/big_picture"
+                android:layout_width="match_parent"
+                android:layout_height="0dp"
+                android:adjustViewBounds="true"
+                android:layout_weight="1"
+                android:layout_marginTop="13dp"
+                android:layout_marginBottom="16dp"
+                android:scaleType="centerCrop"
+                />
+        <ImageView
+                android:layout_width="match_parent"
+                android:layout_height="1dp"
+                android:layout_marginStart="-16dp"
+                android:layout_marginEnd="-16dp"
+                android:id="@+id/action_divider"
+                android:visibility="gone"
+                android:background="@drawable/notification_template_divider" />
+        <include layout="@layout/notification_material_action_list" />
+    </LinearLayout>
 </FrameLayout>
diff --git a/core/res/res/layout/notification_template_material_big_text.xml b/core/res/res/layout/notification_template_material_big_text.xml
index 7ae29fb..354c0fb 100644
--- a/core/res/res/layout/notification_template_material_big_text.xml
+++ b/core/res/res/layout/notification_template_material_big_text.xml
@@ -21,31 +21,30 @@
     android:layout_height="wrap_content"
     android:tag="bigText"
     >
-    <include layout="@layout/notification_template_icon_group"
-        android:layout_width="@dimen/notification_large_icon_width"
-        android:layout_height="@dimen/notification_large_icon_height"
-        />
+    <include layout="@layout/notification_template_header" />
     <LinearLayout
         android:id="@+id/notification_main_column"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_gravity="top"
-        android:layout_marginStart="@dimen/notification_large_icon_width"
-        android:minHeight="@dimen/notification_large_icon_height"
+        android:paddingStart="@dimen/notification_content_margin_start"
+        android:paddingEnd="@dimen/notification_content_margin_end"
+        android:layout_marginTop="@dimen/notification_content_margin_top"
+        android:clipToPadding="false"
+        android:minHeight="@dimen/notification_min_content_height"
         android:orientation="vertical"
         >
         <include layout="@layout/notification_template_part_line1" />
-        <include layout="@layout/notification_template_part_line2" />
+        <include layout="@layout/notification_template_progress" />
         <LinearLayout
             android:layout_width="match_parent"
             android:layout_height="0dp"
-            android:layout_marginEnd="8dp"
-            android:layout_marginBottom="10dp"
+            android:paddingBottom="13dp"
             android:orientation="horizontal"
             android:gravity="top"
             android:layout_weight="1"
             >
-            <TextView android:id="@+id/big_text"
+            <com.android.internal.widget.ImageFloatingTextView android:id="@+id/big_text"
                 android:textAppearance="@style/TextAppearance.Material.Notification"
                 android:layout_width="0dp"
                 android:layout_height="wrap_content"
@@ -66,27 +65,12 @@
         <ImageView
             android:layout_width="match_parent"
             android:layout_height="1dp"
+            android:layout_marginStart="-16dp"
+            android:layout_marginEnd="-16dp"
             android:id="@+id/action_divider"
             android:visibility="gone"
             android:background="@drawable/notification_template_divider" />
-        <include
-            layout="@layout/notification_material_action_list"
-            android:layout_marginStart="-8dp"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            />
-        <ImageView
-            android:layout_width="match_parent"
-            android:layout_height="1dip"
-            android:id="@+id/overflow_divider"
-            android:visibility="visible"
-            android:background="@drawable/notification_template_divider" />
-        <include
-            layout="@layout/notification_template_part_line3"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_marginEnd="8dp"
-            android:layout_marginTop="8dp"
-            android:layout_marginBottom="10dp" />
+        <include layout="@layout/notification_material_action_list" />
     </LinearLayout>
+    <include layout="@layout/notification_template_right_icon" />
 </FrameLayout>
diff --git a/core/res/res/layout/notification_template_material_inbox.xml b/core/res/res/layout/notification_template_material_inbox.xml
index 950ae40..4f12d76 100644
--- a/core/res/res/layout/notification_template_material_inbox.xml
+++ b/core/res/res/layout/notification_template_material_inbox.xml
@@ -21,21 +21,26 @@
     android:layout_height="wrap_content"
     android:tag="inbox"
     >
-    <include layout="@layout/notification_template_icon_group"
-        android:layout_width="@dimen/notification_large_icon_width"
-        android:layout_height="@dimen/notification_large_icon_height"
-        />
+    <include layout="@layout/notification_template_header" />
     <LinearLayout
         android:id="@+id/notification_main_column"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_gravity="top"
-        android:layout_marginStart="@dimen/notification_large_icon_width"
-        android:minHeight="@dimen/notification_large_icon_height"
+        android:paddingStart="@dimen/notification_content_margin_start"
+        android:paddingEnd="@dimen/notification_content_margin_end"
+        android:layout_marginTop="@dimen/notification_content_margin_top"
+        android:minHeight="@dimen/notification_min_content_height"
+        android:clipToPadding="false"
         android:orientation="vertical"
         >
-        <include layout="@layout/notification_template_part_line1" />
-        <include layout="@layout/notification_template_part_line2" />
+        <include layout="@layout/notification_template_part_line1"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content" />
+        <include layout="@layout/notification_template_progress"
+            android:layout_width="match_parent"
+            android:layout_height="15dp"
+            android:layout_marginTop="4dp"/>
 
         <!-- We can't have another vertical linear layout here with weight != 0 so this forces us to
              put the badge on the first line. -->
@@ -43,7 +48,6 @@
             android:layout_width="match_parent"
             android:layout_weight="1"
             android:layout_height="0dp"
-            android:layout_marginEnd="8dp"
             android:orientation="horizontal"
             >
             <TextView android:id="@+id/inbox_text0"
@@ -69,7 +73,6 @@
             android:textAppearance="@style/TextAppearance.Material.Notification"
             android:layout_width="match_parent"
             android:layout_height="0dp"
-            android:layout_marginEnd="8dp"
             android:singleLine="true"
             android:ellipsize="end"
             android:visibility="gone"
@@ -79,7 +82,6 @@
             android:textAppearance="@style/TextAppearance.Material.Notification"
             android:layout_width="match_parent"
             android:layout_height="0dp"
-            android:layout_marginEnd="8dp"
             android:singleLine="true"
             android:ellipsize="end"
             android:visibility="gone"
@@ -89,7 +91,6 @@
             android:textAppearance="@style/TextAppearance.Material.Notification"
             android:layout_width="match_parent"
             android:layout_height="0dp"
-            android:layout_marginEnd="8dp"
             android:singleLine="true"
             android:ellipsize="end"
             android:visibility="gone"
@@ -99,7 +100,6 @@
             android:textAppearance="@style/TextAppearance.Material.Notification"
             android:layout_width="match_parent"
             android:layout_height="0dp"
-            android:layout_marginEnd="8dp"
             android:singleLine="true"
             android:ellipsize="end"
             android:visibility="gone"
@@ -109,7 +109,6 @@
             android:textAppearance="@style/TextAppearance.Material.Notification"
             android:layout_width="match_parent"
             android:layout_height="0dp"
-            android:layout_marginEnd="8dp"
             android:singleLine="true"
             android:ellipsize="end"
             android:visibility="gone"
@@ -119,27 +118,15 @@
             android:textAppearance="@style/TextAppearance.Material.Notification"
             android:layout_width="match_parent"
             android:layout_height="0dp"
-            android:layout_marginEnd="8dp"
             android:singleLine="true"
             android:ellipsize="end"
             android:visibility="gone"
             android:layout_weight="1"
             />
-        <TextView android:id="@+id/inbox_more"
-            android:textAppearance="@style/TextAppearance.Material.Notification"
-            android:layout_width="match_parent"
-            android:layout_height="0dp"
-            android:layout_marginEnd="8dp"
-            android:singleLine="true"
-            android:ellipsize="end"
-            android:visibility="gone"
-            android:layout_weight="1"
-            android:text="@android:string/notification_inbox_ellipsis"
-            />
         <FrameLayout
             android:id="@+id/inbox_end_pad"
             android:layout_width="match_parent"
-            android:layout_height="10dp"
+            android:layout_height="13dp"
             android:visibility="gone"
             android:layout_weight="0"
         />
@@ -148,27 +135,10 @@
             android:layout_height="1dip"
             android:id="@+id/action_divider"
             android:visibility="gone"
+            android:layout_marginStart="-16dp"
+            android:layout_marginEnd="-16dp"
             android:background="@drawable/notification_template_divider" />
-        <include
-            layout="@layout/notification_material_action_list"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_marginLeft="-8dp"
-            android:layout_marginRight="-8dp"
-            android:layout_weight="0"
-            />
-        <ImageView
-            android:layout_width="match_parent"
-            android:layout_height="1dip"
-            android:id="@+id/overflow_divider"
-            android:visibility="visible"
-            android:background="@drawable/notification_template_divider" />
-        <include
-            layout="@layout/notification_template_part_line3"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_marginEnd="8dp"
-            android:layout_marginTop="8dp"
-            android:layout_marginBottom="10dp" />
+        <include layout="@layout/notification_material_action_list" />
     </LinearLayout>
+    <include layout="@layout/notification_template_right_icon" />
 </FrameLayout>
diff --git a/core/res/res/layout/notification_template_material_media.xml b/core/res/res/layout/notification_template_material_media.xml
index f6c22c8..dc4afb8 100644
--- a/core/res/res/layout/notification_template_material_media.xml
+++ b/core/res/res/layout/notification_template_material_media.xml
@@ -15,40 +15,52 @@
   ~ limitations under the License
   -->
 
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<FrameLayout
     android:id="@+id/status_bar_latest_event_content"
+    xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
-    android:layout_height="64dp"
-    android:orientation="horizontal"
-    android:background="#00000000"
+    android:layout_height="wrap_content"
     android:tag="media"
     >
-    <include layout="@layout/notification_template_icon_group"
-        android:layout_width="@dimen/notification_large_icon_width"
-        android:layout_height="@dimen/notification_large_icon_height"
-        android:layout_weight="0"
-        />
+    <include layout="@layout/notification_template_header"
+        android:layout_width="fill_parent"
+        android:layout_height="48dp"
+        android:layout_marginEnd="106dp"/>
     <LinearLayout
+        android:id="@+id/notification_main_column"
         android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_weight="1"
-        android:layout_gravity="fill_vertical"
-        android:minHeight="@dimen/notification_large_icon_height"
-        android:orientation="vertical"
-        >
-        <include layout="@layout/notification_template_part_line1" />
-        <include layout="@layout/notification_template_part_line2" />
-        <include layout="@layout/notification_template_part_line3" />
-    </LinearLayout>
-    <LinearLayout
-        android:id="@+id/media_actions"
-        android:layout_width="wrap_content"
-        android:layout_height="match_parent"
-        android:layout_marginEnd="6dp"
-        android:layout_gravity="center_vertical|end"
+        android:layout_height="@dimen/notification_min_content_height"
+        android:background="#00000000"
         android:orientation="horizontal"
-        android:layoutDirection="ltr"
+        android:layout_marginStart="@dimen/notification_content_margin_start"
+        android:layout_marginTop="@dimen/notification_content_margin_top"
+        android:layout_marginEnd="72dp"
+        android:tag="media"
         >
-        <!-- media buttons will be added here -->
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_gravity="fill_vertical"
+            android:layout_weight="1"
+            android:minHeight="@dimen/notification_min_content_height"
+            android:orientation="vertical"
+            >
+            <include layout="@layout/notification_template_part_line1"/>
+            <include layout="@layout/notification_template_progress"/>
+            <include layout="@layout/notification_template_part_line3"/>
+        </LinearLayout>
+        <LinearLayout
+            android:id="@+id/media_actions"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:layout_gravity="bottom|end"
+            android:layout_marginStart="10dp"
+            android:layout_marginBottom="8dp"
+            android:layoutDirection="ltr"
+            android:orientation="horizontal"
+            >
+            <!-- media buttons will be added here -->
+        </LinearLayout>
     </LinearLayout>
-</LinearLayout>
+    <include layout="@layout/notification_template_right_icon" />
+</FrameLayout>
diff --git a/core/res/res/layout/notification_template_part_chronometer.xml b/core/res/res/layout/notification_template_part_chronometer.xml
index 1f0430e..c5ffbea 100644
--- a/core/res/res/layout/notification_template_part_chronometer.xml
+++ b/core/res/res/layout/notification_template_part_chronometer.xml
@@ -18,9 +18,7 @@
     android:textAppearance="@style/TextAppearance.Material.Notification.Time"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
-    android:layout_gravity="center"
+    android:layout_marginEnd="4dp"
     android:layout_weight="0"
     android:singleLine="true"
-    android:gravity="center"
-    android:paddingStart="8dp"
     />
diff --git a/core/res/res/layout/notification_template_part_line1.xml b/core/res/res/layout/notification_template_part_line1.xml
index 78bc1ed..e7ac408 100644
--- a/core/res/res/layout/notification_template_part_line1.xml
+++ b/core/res/res/layout/notification_template_part_line1.xml
@@ -19,30 +19,25 @@
     android:id="@+id/line1"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:layout_marginEnd="8dp"
     android:orientation="horizontal"
+    android:layout_marginBottom="1dp"
     >
     <TextView android:id="@+id/title"
         android:textAppearance="@style/TextAppearance.Material.Notification.Title"
-        android:layout_width="0dp"
+        android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:singleLine="true"
         android:ellipsize="marquee"
         android:fadingEdge="horizontal"
-        android:layout_weight="1"
         />
-    <ViewStub android:id="@+id/time"
-        android:layout_width="wrap_content"
+    <TextView android:id="@+id/text_line_1"
+        android:textAppearance="@style/TextAppearance.Material.Notification"
+        android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:layout_weight="0"
-        android:visibility="gone"
-        android:layout="@layout/notification_template_part_time"
-        />
-    <ViewStub android:id="@+id/chronometer"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_weight="0"
-        android:visibility="gone"
-        android:layout="@layout/notification_template_part_chronometer"
+        android:gravity="end|bottom"
+        android:layout_marginStart="16dp"
+        android:singleLine="true"
+        android:ellipsize="marquee"
+        android:fadingEdge="horizontal"
         />
 </LinearLayout>
diff --git a/core/res/res/layout/notification_template_part_line2.xml b/core/res/res/layout/notification_template_part_line2.xml
deleted file mode 100644
index db43271..0000000
--- a/core/res/res/layout/notification_template_part_line2.xml
+++ /dev/null
@@ -1,58 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ 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
-  -->
-
-<merge xmlns:android="http://schemas.android.com/apk/res/android">
-    <LinearLayout
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_marginEnd="8dp"
-        android:orientation="horizontal"
-        android:gravity="center_vertical"
-        >
-        <TextView
-            android:id="@+id/text2"
-            android:textAppearance="@style/TextAppearance.Material.Notification.Line2"
-            android:layout_width="0dp"
-            android:layout_height="wrap_content"
-            android:layout_marginTop="-1dp"
-            android:layout_marginBottom="-1dp"
-            android:singleLine="true"
-            android:fadingEdge="horizontal"
-            android:ellipsize="marquee"
-            android:visibility="gone"
-            android:layout_weight="1"
-        />
-        <ImageView android:id="@+id/profile_badge_line2"
-            android:layout_width="@dimen/notification_badge_size"
-            android:layout_height="@dimen/notification_badge_size"
-            android:layout_weight="0"
-            android:layout_marginStart="4dp"
-            android:scaleType="fitCenter"
-            android:visibility="gone"
-            android:contentDescription="@string/notification_work_profile_content_description"
-            />
-    </LinearLayout>
-    <ViewStub
-        android:id="@android:id/progress"
-        android:layout="@layout/notification_template_progressbar"
-        android:layout_width="match_parent"
-        android:layout_height="15dp"
-        android:layout_marginEnd="8dp"
-        android:visibility="gone"
-        android:layout_weight="0"
-        />
-</merge>
diff --git a/core/res/res/layout/notification_template_part_line3.xml b/core/res/res/layout/notification_template_part_line3.xml
index da3c5c5..76337ac 100644
--- a/core/res/res/layout/notification_template_part_line3.xml
+++ b/core/res/res/layout/notification_template_part_line3.xml
@@ -19,7 +19,6 @@
     android:id="@+id/line3"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:layout_marginEnd="8dp"
     android:orientation="horizontal"
     android:gravity="center_vertical"
     >
@@ -33,16 +32,6 @@
         android:ellipsize="marquee"
         android:fadingEdge="horizontal"
         />
-    <TextView android:id="@+id/info"
-        android:textAppearance="@style/TextAppearance.Material.Notification.Info"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_gravity="center"
-        android:layout_weight="0"
-        android:singleLine="true"
-        android:gravity="center"
-        android:paddingStart="8dp"
-        />
     <ImageView android:id="@+id/profile_badge_line3"
         android:layout_width="@dimen/notification_badge_size"
         android:layout_height="@dimen/notification_badge_size"
diff --git a/core/res/res/layout/notification_template_part_time.xml b/core/res/res/layout/notification_template_part_time.xml
index 37c7ebe..442ff8c 100644
--- a/core/res/res/layout/notification_template_part_time.xml
+++ b/core/res/res/layout/notification_template_part_time.xml
@@ -19,8 +19,7 @@
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:layout_gravity="center"
+    android:layout_marginEnd="4dp"
     android:layout_weight="0"
     android:singleLine="true"
-    android:gravity="center"
-    android:paddingStart="8dp"
     />
diff --git a/core/res/res/drawable/notification_icon_legacy_bg.xml b/core/res/res/layout/notification_template_progress.xml
similarity index 64%
rename from core/res/res/drawable/notification_icon_legacy_bg.xml
rename to core/res/res/layout/notification_template_progress.xml
index cc5755d..85532ad 100644
--- a/core/res/res/drawable/notification_icon_legacy_bg.xml
+++ b/core/res/res/layout/notification_template_progress.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-  ~ Copyright (C) 2014 The Android Open Source Project
+  ~ 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.
@@ -14,9 +14,11 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License
   -->
-
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
-       android:shape="oval">
-    <solid
-            android:color="@color/notification_icon_bg_color"/>
-</shape>
+<ViewStub
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@android:id/progress"
+    android:layout="@layout/notification_template_progressbar"
+    android:layout_width="match_parent"
+    android:layout_height="15dp"
+    android:visibility="gone"
+    />
diff --git a/core/res/res/drawable/notification_icon_legacy_bg.xml b/core/res/res/layout/notification_template_right_icon.xml
similarity index 62%
copy from core/res/res/drawable/notification_icon_legacy_bg.xml
copy to core/res/res/layout/notification_template_right_icon.xml
index cc5755d..3b358ab 100644
--- a/core/res/res/drawable/notification_icon_legacy_bg.xml
+++ b/core/res/res/layout/notification_template_right_icon.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-  ~ Copyright (C) 2014 The Android Open Source Project
+  ~ 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.
@@ -15,8 +15,13 @@
   ~ limitations under the License
   -->
 
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
-       android:shape="oval">
-    <solid
-            android:color="@color/notification_icon_bg_color"/>
-</shape>
+<ImageView android:id="@+id/right_icon" xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="40dp"
+    android:layout_height="40dp"
+    android:layout_marginEnd="16dp"
+    android:layout_marginTop="32dp"
+    android:layout_gravity="top|end"
+    android:scaleType="centerCrop"
+    />
+
+
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/colors.xml b/core/res/res/values/colors.xml
index 7f8c460..dad68ba 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -130,12 +130,9 @@
     <drawable name="notification_template_divider">#29000000</drawable>
     <drawable name="notification_template_divider_media">#29ffffff</drawable>
 
-    <color name="notification_icon_bg_color">#ff9e9e9e</color>
+    <color name="notification_icon_default_color">#ff616161</color>
     <color name="notification_action_color_filter">@color/secondary_text_material_light</color>
 
-    <color name="notification_media_primary_color">@color/primary_text_material_dark</color>
-    <color name="notification_media_secondary_color">@color/secondary_text_material_dark</color>
-
     <color name="notification_progress_background_color">@color/secondary_text_material_light</color>
 
     <!-- Keyguard colors -->
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 01daf26..7b4becc 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -130,11 +130,38 @@
     <!-- Default padding for dialogs. -->
     <dimen name="dialog_padding">16dp</dimen>
 
+    <!-- The margin on the start of the content view -->
+    <dimen name="notification_content_margin_start">16dp</dimen>
+
+    <!-- The margin on the end of the content view -->
+    <dimen name="notification_content_margin_end">16dp</dimen>
+
+    <!-- The margin on the end of the content view with a picture.-->
+    <dimen name="notification_content_picture_margin">56dp</dimen>
+
+    <!-- height of the content margin to accomodate for the header -->
+    <dimen name="notification_content_margin_top">30dp</dimen>
+
+    <!-- height of notification header view if present -->
+    <dimen name="notification_header_height">32dp</dimen>
+
+    <!-- Height of a small notification in the status bar -->
+    <dimen name="notification_min_height">84dp</dimen>
+
     <!-- The width of the big icons in notifications. -->
     <dimen name="notification_large_icon_width">64dp</dimen>
     <!-- The width of the big icons in notifications. -->
     <dimen name="notification_large_icon_height">64dp</dimen>
 
+    <!--  Min height of the notification content. -->
+    <dimen name="notification_min_content_height">54dp</dimen>
+
+    <!-- The minimum width of the app name in the header if it shrinks -->
+    <dimen name="notification_header_shrink_min_width">72dp</dimen>
+
+    <!-- The minimum height of the content if there is a picture present with big picture -->
+    <dimen name="notification_big_picture_content_min_height_with_picture">41dp</dimen>
+
     <!-- Minimum width of the search view text entry area. -->
     <dimen name="search_view_text_min_width">160dip</dimen>
 
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 8a00294..d6dd842 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -531,6 +531,12 @@
          [CHAR LIMIT=4] -->
     <string name="status_bar_notification_info_overflow">999+</string>
 
+    <!-- The number of notifications in the notification header. An example would be (2) or (12) -->
+    <string name="notification_children_count_bracketed">(<xliff:g id="notificationCount" example="1">%d</xliff:g>)</string>
+
+    <!-- The divider symbol between different parts of the notification header. not translatable [CHAR LIMIT=1] -->
+    <string name="notification_header_divider_symbol" translatable="false">•</string>
+
     <!-- Displayed to the user to tell them that they have started up the phone in "safe mode" -->
     <string name="safeMode">Safe mode</string>
 
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 8cf1134..3860f68 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -197,11 +197,8 @@
   <java-symbol type="id" name="inbox_text4" />
   <java-symbol type="id" name="inbox_text5" />
   <java-symbol type="id" name="inbox_text6" />
-  <java-symbol type="id" name="inbox_more" />
-  <java-symbol type="id" name="inbox_end_pad" />
   <java-symbol type="id" name="status_bar_latest_event_content" />
   <java-symbol type="id" name="action_divider" />
-  <java-symbol type="id" name="overflow_divider" />
   <java-symbol type="id" name="notification_main_column" />
   <java-symbol type="id" name="sms_short_code_confirm_message" />
   <java-symbol type="id" name="sms_short_code_detail_layout" />
@@ -218,7 +215,6 @@
   <java-symbol type="id" name="pin_error_message" />
   <java-symbol type="id" name="timePickerLayout" />
   <java-symbol type="id" name="profile_badge_large_template" />
-  <java-symbol type="id" name="profile_badge_line2" />
   <java-symbol type="id" name="profile_badge_line3" />
   <java-symbol type="id" name="transitionPosition" />
   <java-symbol type="id" name="selection_start_handle" />
@@ -1877,15 +1873,11 @@
   <java-symbol type="layout" name="notification_template_material_inbox" />
   <java-symbol type="layout" name="notification_template_material_media" />
   <java-symbol type="layout" name="notification_template_material_big_media" />
-  <java-symbol type="layout" name="notification_template_material_big_media_narrow" />
   <java-symbol type="layout" name="notification_template_material_big_text" />
-  <java-symbol type="layout" name="notification_template_icon_group" />
+  <java-symbol type="layout" name="notification_template_header" />
   <java-symbol type="layout" name="notification_material_media_action" />
   <java-symbol type="color" name="notification_action_color_filter" />
-  <java-symbol type="color" name="notification_icon_bg_color" />
-  <java-symbol type="drawable" name="notification_icon_legacy_bg" />
-  <java-symbol type="color" name="notification_media_primary_color" />
-  <java-symbol type="color" name="notification_media_secondary_color" />
+  <java-symbol type="color" name="notification_icon_default_color" />
   <java-symbol type="color" name="notification_progress_background_color" />
   <java-symbol type="id" name="media_actions" />
 
@@ -2276,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" />
@@ -2287,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" />
@@ -2352,4 +2349,24 @@
   <java-symbol type="id" name="suggestionContainer" />
   <java-symbol type="id" name="addToDictionaryButton" />
   <java-symbol type="id" name="deleteButton" />
+
+  <java-symbol type="string" name="notification_children_count_bracketed" />
+  <java-symbol type="id" name="app_name_text" />
+  <java-symbol type="id" name="number_of_children" />
+  <java-symbol type="id" name="header_sub_text" />
+  <java-symbol type="id" name="expand_button" />
+  <java-symbol type="id" name="notification_header" />
+  <java-symbol type="id" name="header_content_info" />
+  <java-symbol type="id" name="time_divider" />
+  <java-symbol type="id" name="sub_text_divider" />
+  <java-symbol type="id" name="content_info_divider" />
+  <java-symbol type="id" name="text_line_1" />
+  <java-symbol type="drawable" name="ic_arrow_up_14dp" />
+  <java-symbol type="dimen" name="notification_header_height" />
+  <java-symbol type="dimen" name="notification_big_picture_content_min_height_with_picture" />
+  <java-symbol type="dimen" name="notification_header_shrink_min_width" />
+  <java-symbol type="dimen" name="notification_content_margin_start" />
+  <java-symbol type="dimen" name="notification_content_margin_end" />
+  <java-symbol type="dimen" name="notification_content_picture_margin" />
+  <java-symbol type="dimen" name="notification_content_margin_top" />
 </resources>
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/TextViewActivityTest.java b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
index 78a0a59..4614505 100644
--- a/core/tests/coretests/src/android/widget/TextViewActivityTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
@@ -34,12 +34,15 @@
 import static android.support.test.espresso.matcher.RootMatchers.withDecorView;
 import static android.support.test.espresso.matcher.ViewMatchers.hasDescendant;
 import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom;
+import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
 import static android.support.test.espresso.matcher.ViewMatchers.withId;
 import static android.support.test.espresso.matcher.ViewMatchers.withText;
 import static org.hamcrest.Matchers.allOf;
 
 import com.android.frameworks.coretests.R;
 
+import android.support.test.espresso.NoMatchingRootException;
+import android.support.test.espresso.NoMatchingViewException;
 import android.support.test.espresso.ViewInteraction;
 import android.test.ActivityInstrumentationTestCase2;
 import android.test.suitebuilder.annotation.SmallTest;
@@ -165,18 +168,21 @@
         assertFloatingToolbarIsDisplayed(getActivity());
     }
 
-    private static ViewInteraction onHandleView(int id) {
-        return onView(allOf(withId(id), isAssignableFrom(Editor.HandleView.class)))
-                .inRoot(withDecorView(hasDescendant(withId(id))));
-    }
-
     @SmallTest
     public void testSelectionHandles() throws Exception {
         final String text = "abcd efg hijk lmn";
         onView(withId(R.id.textview)).perform(click());
         onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(text));
+
+        assertNoSelectionHandles();
+
         onView(withId(R.id.textview)).perform(doubleClickOnTextAtIndex(text.indexOf('f')));
 
+        onHandleView(com.android.internal.R.id.selection_start_handle)
+                .check(matches(isDisplayed()));
+        onHandleView(com.android.internal.R.id.selection_end_handle)
+                .check(matches(isDisplayed()));
+
         final TextView textView = (TextView)getActivity().findViewById(R.id.textview);
         onHandleView(com.android.internal.R.id.selection_start_handle)
                 .perform(dragHandle(textView, Handle.SELECTION_START, text.indexOf('a')));
@@ -336,4 +342,25 @@
                 .perform(dragHandle(textView, Handle.SELECTION_END, text.indexOf('i')));
         onView(withId(R.id.textview)).check(hasSelection("hijk"));
     }
+
+    private static void assertNoSelectionHandles() {
+        try {
+            onHandleView(com.android.internal.R.id.selection_start_handle)
+                    .check(matches(isDisplayed()));
+        } catch (NoMatchingRootException | NoMatchingViewException | AssertionError e) {
+            try {
+                onHandleView(com.android.internal.R.id.selection_end_handle)
+                        .check(matches(isDisplayed()));
+            } catch (NoMatchingRootException | NoMatchingViewException | AssertionError e1) {
+                return;
+            }
+        }
+        throw new AssertionError("Selection handle found");
+    }
+
+    private static ViewInteraction onHandleView(int id)
+            throws NoMatchingRootException, NoMatchingViewException, AssertionError {
+        return onView(allOf(withId(id), isAssignableFrom(Editor.HandleView.class)))
+                .inRoot(withDecorView(hasDescendant(withId(id))));
+    }
 }
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..63e310e 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,
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/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/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/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/StubProvider.java b/packages/DocumentsUI/tests/src/com/android/documentsui/StubProvider.java
index 2d42ddc..d23cdeb 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/StubProvider.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/StubProvider.java
@@ -531,6 +531,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/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 5547771..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,10 +37,8 @@
     @Override
     public void setUp() throws Exception {
         mUsbManager = getContext().getSystemService(UsbManager.class);
-        mUsbDevice = findDevice();
         mManager = new MtpManager(getContext());
-        mManager.openDevice(mUsbDevice.getDeviceId());
-        waitForStorages(mManager, mUsbDevice.getDeviceId());
+        mUsbDevice = TestUtil.setupMtpDevice(getInstrumentation(), mUsbManager, mManager);
     }
 
     @Override
@@ -56,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() {
@@ -64,7 +59,7 @@
                 try {
                     mManager.readEvent(mUsbDevice.getDeviceId(), signal);
                 } catch (OperationCanceledException | IOException e) {
-                    show(e.getMessage());
+                    getInstrumentation().show(e.getMessage());
                 }
             }
         };
@@ -74,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..b92a389 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 != Float.NaN) {
+                    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/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
index e28b2e5..2b1c66d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
@@ -19,8 +19,12 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.net.ConnectivityManager;
+import android.net.Network;
+import android.net.NetworkCapabilities;
 import android.net.NetworkInfo;
 import android.net.NetworkInfo.DetailedState;
+import android.net.NetworkRequest;
 import android.net.wifi.ScanResult;
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiInfo;
@@ -62,6 +66,9 @@
     private final Context mContext;
     private final WifiManager mWifiManager;
     private final IntentFilter mFilter;
+    private final ConnectivityManager mConnectivityManager;
+    private final NetworkRequest mNetworkRequest;
+    private WifiTrackerNetworkCallback mNetworkCallback;
 
     private final AtomicBoolean mConnected = new AtomicBoolean(false);
     private final WifiListener mListener;
@@ -104,13 +111,15 @@
     public WifiTracker(Context context, WifiListener wifiListener, Looper workerLooper,
             boolean includeSaved, boolean includeScans, boolean includePasspoints) {
         this(context, wifiListener, workerLooper, includeSaved, includeScans, includePasspoints,
-                (WifiManager) context.getSystemService(Context.WIFI_SERVICE), Looper.myLooper());
+                context.getSystemService(WifiManager.class),
+                context.getSystemService(ConnectivityManager.class), Looper.myLooper());
     }
 
     @VisibleForTesting
     WifiTracker(Context context, WifiListener wifiListener, Looper workerLooper,
             boolean includeSaved, boolean includeScans, boolean includePasspoints,
-            WifiManager wifiManager, Looper currentLooper) {
+            WifiManager wifiManager, ConnectivityManager connectivityManager,
+            Looper currentLooper) {
         if (!includeSaved && !includeScans) {
             throw new IllegalArgumentException("Must include either saved or scans");
         }
@@ -127,6 +136,7 @@
         mIncludeScans = includeScans;
         mIncludePasspoints = includePasspoints;
         mListener = wifiListener;
+        mConnectivityManager = connectivityManager;
 
         // check if verbose logging has been turned on or off
         sVerboseLogging = mWifiManager.getVerboseLoggingLevel();
@@ -139,7 +149,11 @@
         mFilter.addAction(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION);
         mFilter.addAction(WifiManager.LINK_CONFIGURATION_CHANGED_ACTION);
         mFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
-        mFilter.addAction(WifiManager.RSSI_CHANGED_ACTION);
+
+        mNetworkRequest = new NetworkRequest.Builder()
+                .clearCapabilities()
+                .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
+                .build();
     }
 
     /**
@@ -192,6 +206,9 @@
         resumeScanning();
         if (!mRegistered) {
             mContext.registerReceiver(mReceiver, mFilter);
+            // NetworkCallback objects cannot be reused. http://b/20701525 .
+            mNetworkCallback = new WifiTrackerNetworkCallback();
+            mConnectivityManager.registerNetworkCallback(mNetworkRequest, mNetworkCallback);
             mRegistered = true;
         }
     }
@@ -207,6 +224,7 @@
             mWorkHandler.removeMessages(WorkHandler.MSG_UPDATE_ACCESS_POINTS);
             mWorkHandler.removeMessages(WorkHandler.MSG_UPDATE_NETWORK_INFO);
             mContext.unregisterReceiver(mReceiver);
+            mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
             mRegistered = false;
         }
         pauseScanning();
@@ -461,12 +479,12 @@
             mMainHandler.sendEmptyMessage(MainHandler.MSG_RESUME_SCANNING);
         }
 
-        mLastInfo = mWifiManager.getConnectionInfo();
         if (networkInfo != null) {
             mLastNetworkInfo = networkInfo;
         }
 
         WifiConfiguration connectionConfig = null;
+        mLastInfo = mWifiManager.getConnectionInfo();
         if (mLastInfo != null) {
             connectionConfig = getWifiConfigurationForNetworkId(mLastInfo.getNetworkId());
         }
@@ -532,12 +550,21 @@
                 mWorkHandler.sendEmptyMessage(WorkHandler.MSG_UPDATE_ACCESS_POINTS);
                 mWorkHandler.obtainMessage(WorkHandler.MSG_UPDATE_NETWORK_INFO, info)
                         .sendToTarget();
-            } else if (WifiManager.RSSI_CHANGED_ACTION.equals(action)) {
-                mWorkHandler.sendEmptyMessage(WorkHandler.MSG_UPDATE_NETWORK_INFO);
             }
         }
     };
 
+    private final class WifiTrackerNetworkCallback extends ConnectivityManager.NetworkCallback {
+        public void onCapabilitiesChanged(Network network, NetworkCapabilities nc) {
+            if (network.equals(mWifiManager.getCurrentNetwork())) {
+                // We don't send a NetworkInfo object along with this message, because even if we
+                // fetch one from ConnectivityManager, it might be older than the most recent
+                // NetworkInfo message we got via a WIFI_STATE_CHANGED broadcast.
+                mWorkHandler.sendEmptyMessage(WorkHandler.MSG_UPDATE_NETWORK_INFO);
+            }
+        }
+    }
+
     private final class MainHandler extends Handler {
         private static final int MSG_CONNECTED_CHANGED = 0;
         private static final int MSG_WIFI_STATE_CHANGED = 1;
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/SystemUI/res/drawable/notification_expand_more.xml b/packages/SystemUI/res/drawable/notification_expand_more.xml
index 5aa7937..430fb0d 100644
--- a/packages/SystemUI/res/drawable/notification_expand_more.xml
+++ b/packages/SystemUI/res/drawable/notification_expand_more.xml
@@ -12,7 +12,7 @@
     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.
+    limitations under the License._more
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
     android:width="22.0dp"
diff --git a/packages/SystemUI/res/layout/notification_header.xml b/packages/SystemUI/res/layout/notification_header.xml
deleted file mode 100644
index 3475d00..0000000
--- a/packages/SystemUI/res/layout/notification_header.xml
+++ /dev/null
@@ -1,83 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ 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
-  -->
-
-<com.android.systemui.statusbar.notification.NotificationHeaderView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="@dimen/notification_header_height"
-    android:clipChildren="false">
-    <LinearLayout
-        android:orientation="horizontal"
-        android:layout_width="wrap_content"
-        android:layout_height="@dimen/notification_header_height"
-        android:layout_gravity="start"
-        android:gravity="center_vertical"
-        android:paddingStart="@dimen/notification_content_margin_start">
-        <ImageView
-            android:id="@+id/header_notification_icon"
-            android:layout_width="24dp"
-            android:layout_height="24dp"
-            android:layout_marginEnd="4dp"
-            />
-        <TextView
-            android:id="@+id/number_of_children"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:textAppearance="@*android:style/TextAppearance.Material.Notification"
-            android:layout_marginEnd="2dp"
-            android:layout_marginStart="1dp"
-            />
-        <TextView
-            android:id="@+id/app_name_text"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:textAppearance="@style/TextAppearance.Material.Notification.HeaderTitle"
-            android:layout_marginStart="4dp"
-            android:layout_marginEnd="8dp"
-            />
-        <TextView
-            android:id="@+id/app_title_sub_text_divider"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:textAppearance="@style/TextAppearance.Material.Notification.HeaderTitle"
-            android:layout_marginEnd="8dp"
-            android:text="@string/notification_header_divider_symbol"/>
-        <TextView
-            android:id="@+id/title_sub_text"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:textAppearance="@style/TextAppearance.Material.Notification.HeaderTitle"
-            android:layout_marginEnd="8dp" />
-        <TextView
-            android:id="@+id/post_time"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_gravity="end|center_vertical"
-            android:textAppearance="@*android:style/TextAppearance.Material.Notification.Time"
-            android:paddingEnd="8dp"
-            />
-    </LinearLayout>
-    <ImageButton
-        android:id="@+id/notification_expand_button"
-        android:background="@null"
-        android:layout_width="48dp"
-        android:layout_height="48dp"
-        android:layout_gravity="end|center_vertical"
-        android:src="@drawable/notification_expand_more"
-        android:layout_marginEnd="8dp"
-        />
-</com.android.systemui.statusbar.notification.NotificationHeaderView>
diff --git a/packages/SystemUI/res/layout/recents_history.xml b/packages/SystemUI/res/layout/recents_history.xml
index de70d30..b65a5c5 100644
--- a/packages/SystemUI/res/layout/recents_history.xml
+++ b/packages/SystemUI/res/layout/recents_history.xml
@@ -19,16 +19,6 @@
     android:layout_height="match_parent"
     android:background="#99000000"
     android:orientation="vertical">
-    <TextView
-        xmlns:android="http://schemas.android.com/apk/res/android"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:padding="14dp"
-        android:gravity="start"
-        android:text="@string/recents_history_label"
-        android:textSize="24sp"
-        android:textColor="#FFFFFF"
-        android:fontFamily="sans-serif-medium" />
     <android.support.v7.widget.RecyclerView
         android:id="@+id/list"
         android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/layout/recents_history_button.xml b/packages/SystemUI/res/layout/recents_history_button.xml
index 471f518..601c5ed 100644
--- a/packages/SystemUI/res/layout/recents_history_button.xml
+++ b/packages/SystemUI/res/layout/recents_history_button.xml
@@ -22,5 +22,9 @@
     android:textSize="14sp"
     android:textColor="#FFFFFF"
     android:textAllCaps="true"
+    android:shadowColor="#99000000"
+    android:shadowDx="0"
+    android:shadowDy="2"
+    android:shadowRadius="5"
     android:fontFamily="sans-serif-medium"
     android:visibility="invisible" />
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/status_bar_notification_row.xml b/packages/SystemUI/res/layout/status_bar_notification_row.xml
index b9088ec..0cea7ae 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_row.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_row.xml
@@ -61,14 +61,6 @@
         />
 
     <ViewStub
-        android:layout="@layout/notification_header"
-        android:id="@+id/notification_header_stub"
-        android:inflatedId="@+id/notification_header"
-        android:layout_width="match_parent"
-        android:layout_height="@dimen/notification_header_height"
-        />
-
-    <ViewStub
         android:layout="@layout/notification_guts"
         android:id="@+id/notification_guts_stub"
         android:inflatedId="@+id/notification_guts"
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 26a0577..bfd8af9 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -109,7 +109,6 @@
     <color name="assist_orb_color">#ffffff</color>
 
     <color name="keyguard_user_switcher_background_gradient_color">#77000000</color>
-    <color name="doze_small_icon_background_color">#ff434343</color>
 
     <!-- The color of the navigation bar icons. Need to be in sync with ic_sysbar_* -->
     <color name="navigation_bar_icon_color">#E5FFFFFF</color>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 82192fe..086e9f4 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -48,11 +48,14 @@
     <!-- Height of a single line notification in the status bar -->
     <dimen name="notification_single_line_height">32sp</dimen>
 
-    <!-- Height of a small notification in the status bar -->
-    <dimen name="notification_min_height">64dp</dimen>
+    <!-- Height of a small notification in the status bar-->
+    <dimen name="notification_min_height">84dp</dimen>
+
+    <!-- Height of a small notification in the status bar which was used before android N -->
+    <dimen name="notification_min_height_legacy">64dp</dimen>
 
     <!-- Height of a large notification in the status bar -->
-    <dimen name="notification_max_height">256dp</dimen>
+    <dimen name="notification_max_height">276dp</dimen>
 
     <!-- Height of a medium notification in the status bar -->
     <dimen name="notification_mid_height">128dp</dimen>
@@ -258,7 +261,7 @@
 
     <!-- bottom_stack_peek_amount + notification_min_height
          + notification_collapse_second_card_padding -->
-    <dimen name="min_stack_height">84dp</dimen>
+    <dimen name="min_stack_height">104dp</dimen>
 
     <!-- The height of the area before the bottom stack in which the notifications slow down -->
     <dimen name="bottom_stack_slow_down_length">12dp</dimen>
@@ -276,7 +279,7 @@
     <dimen name="notification_padding_dimmed">0dp</dimen>
 
     <!-- The padding between the individual notification cards. -->
-    <dimen name="notification_padding">4dp</dimen>
+    <dimen name="notification_padding">2dp</dimen>
 
     <!-- The minimum amount of top overscroll to go to the quick settings. -->
     <dimen name="min_top_overscroll_to_qs">36dp</dimen>
@@ -296,7 +299,7 @@
     <!-- Falsing threshold used when dismissing notifications from the lockscreen. -->
     <dimen name="swipe_helper_falsing_threshold">70dp</dimen>
 
-    <dimen name="notifications_top_padding">8dp</dimen>
+    <dimen name="notifications_top_padding">4dp</dimen>
     
     <!-- Minimum distance the user has to drag down to go to the full shade. -->
     <dimen name="keyguard_drag_down_min_distance">100dp</dimen>
@@ -348,9 +351,6 @@
     <!-- radius of the corners of the material rounded rect background but negative-->
     <dimen name="notification_material_rounded_rect_radius_negative">-2dp</dimen>
 
-    <!-- height of notification header view if present -->
-    <dimen name="notification_header_height">32dp</dimen>
-
     <!-- The padding between notification children -->
     <dimen name="notification_children_padding">2dp</dimen>
 
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 04233ba..d6a361c 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -753,9 +753,6 @@
     <!-- Text for overflow card on Keyguard when there is not enough space for all notifications on Keyguard. [CHAR LIMIT=1] -->
     <string name="keyguard_more_overflow_text">+<xliff:g id="number_of_notifications" example="5">%d</xliff:g></string>
 
-    <!-- The divider symbol between different parts of the notification header. not translatable [CHAR LIMIT=1] -->
-    <string name="notification_header_divider_symbol" translatable="false">•</string>
-
     <!-- An explanation for the visual speed bump in the notifications, which will appear when you click on it. [CHAR LIMIT=50] -->
     <string name="speed_bump_explanation">Less urgent notifications below</string>
 
diff --git a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
index d9f7a46..051921a 100644
--- a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
@@ -509,7 +509,7 @@
         if (canBeExpanded) {
             if (DEBUG) Log.d(TAG, "working on an expandable child");
             mNaturalHeight = mScaler.getNaturalHeight();
-            mSmallSize = v.getMinHeight();
+            mSmallSize = v.getMinExpandHeight();
         } else {
             if (DEBUG) Log.d(TAG, "working on a non-expandable child");
             mNaturalHeight = mOldHeight;
diff --git a/packages/SystemUI/src/com/android/systemui/ViewInvertHelper.java b/packages/SystemUI/src/com/android/systemui/ViewInvertHelper.java
index eddf2b1..5b8d3d6 100644
--- a/packages/SystemUI/src/com/android/systemui/ViewInvertHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/ViewInvertHelper.java
@@ -26,6 +26,8 @@
 import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
 
+import java.util.ArrayList;
+
 /**
  * Helper to invert the colors of views and fade between the states.
  */
@@ -33,14 +35,24 @@
 
     private final Paint mDarkPaint = new Paint();
     private final Interpolator mLinearOutSlowInInterpolator;
-    private final View mTarget;
+    private final ArrayList<View> mTargets;
     private final ColorMatrix mMatrix = new ColorMatrix();
     private final ColorMatrix mGrayscaleMatrix = new ColorMatrix();
     private final long mFadeDuration;
 
     public ViewInvertHelper(View target, long fadeDuration) {
-        mTarget = target;
-        mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(mTarget.getContext(),
+        this(constructArray(target), fadeDuration);
+    }
+
+    private static ArrayList<View> constructArray(View target) {
+        final ArrayList<View> views = new ArrayList<>();
+        views.add(target);
+        return views;
+    }
+
+    public ViewInvertHelper(ArrayList<View> targets, long fadeDuration) {
+        mTargets = targets;
+        mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(mTargets.get(0).getContext(),
                 android.R.interpolator.linear_out_slow_in);
         mFadeDuration = fadeDuration;
     }
@@ -53,14 +65,18 @@
             @Override
             public void onAnimationUpdate(ValueAnimator animation) {
                 updateInvertPaint((Float) animation.getAnimatedValue());
-                mTarget.setLayerType(View.LAYER_TYPE_HARDWARE, mDarkPaint);
+                for (int i = 0; i < mTargets.size(); i++) {
+                    mTargets.get(i).setLayerType(View.LAYER_TYPE_HARDWARE, mDarkPaint);
+                }
             }
         });
         animator.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationEnd(Animator animation) {
                 if (!invert) {
-                    mTarget.setLayerType(View.LAYER_TYPE_NONE, null);
+                    for (int i = 0; i < mTargets.size(); i++) {
+                        mTargets.get(i).setLayerType(View.LAYER_TYPE_NONE, null);
+                    }
                 }
             }
         });
@@ -73,16 +89,16 @@
     public void update(boolean invert) {
         if (invert) {
             updateInvertPaint(1f);
-            mTarget.setLayerType(View.LAYER_TYPE_HARDWARE, mDarkPaint);
+            for (int i = 0; i < mTargets.size(); i++) {
+                mTargets.get(i).setLayerType(View.LAYER_TYPE_HARDWARE, mDarkPaint);
+            }
         } else {
-            mTarget.setLayerType(View.LAYER_TYPE_NONE, null);
+            for (int i = 0; i < mTargets.size(); i++) {
+                mTargets.get(i).setLayerType(View.LAYER_TYPE_NONE, null);
+            }
         }
     }
 
-    public View getTarget() {
-        return mTarget;
-    }
-
     private void updateInvertPaint(float intensity) {
         float components = 1 - 2 * intensity;
         final float[] invert = {
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
index a12a3f1..19b65f7 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
@@ -36,7 +36,6 @@
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.util.Slog;
-import android.view.View;
 
 import com.android.systemui.R;
 import com.android.systemui.statusbar.phone.PhoneStatusBar;
@@ -162,9 +161,6 @@
                 .setColor(mContext.getColor(
                         com.android.internal.R.color.system_notification_accent_color));
         final Notification n = nb.build();
-        if (n.headsUpContentView != null) {
-            n.headsUpContentView.setViewVisibility(com.android.internal.R.id.right_icon, View.GONE);
-        }
         mNoMan.notifyAsUser(TAG_NOTIFICATION, R.id.notification_power, n, UserHandle.ALL);
     }
 
@@ -200,9 +196,6 @@
             mPlaySound = false;
         }
         final Notification n = nb.build();
-        if (n.headsUpContentView != null) {
-            n.headsUpContentView.setViewVisibility(com.android.internal.R.id.right_icon, View.GONE);
-        }
         mNoMan.notifyAsUser(TAG_NOTIFICATION, R.id.notification_power, n, UserHandle.ALL);
     }
 
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/RecentsActivityLaunchState.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java
index 8dd9e47..43db666 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java
@@ -82,7 +82,11 @@
             if (launchedFromHome) {
                 return numTasks - 1;
             } else {
-                return numTasks - 2;
+                if (flags.isFastToggleRecentsEnabled()) {
+                    return numTasks - 1;
+                } else {
+                    return numTasks - 2;
+                }
             }
         }
 
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/history/RecentsHistoryView.java b/packages/SystemUI/src/com/android/systemui/recents/history/RecentsHistoryView.java
index f48883f..9d3a99f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/history/RecentsHistoryView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/history/RecentsHistoryView.java
@@ -29,6 +29,8 @@
 import android.widget.LinearLayout;
 
 import com.android.systemui.R;
+import com.android.systemui.recents.Recents;
+import com.android.systemui.recents.RecentsConfiguration;
 import com.android.systemui.recents.misc.ReferenceCountedTrigger;
 import com.android.systemui.recents.model.TaskStack;
 
@@ -43,6 +45,7 @@
     private RecyclerView mRecyclerView;
     private RecentsHistoryAdapter mAdapter;
     private boolean mIsVisible;
+    private Rect mSystemInsets = new Rect();
 
     private Interpolator mFastOutSlowInInterpolator;
     private Interpolator mFastOutLinearInInterpolator;
@@ -123,7 +126,8 @@
      * Updates the system insets of this history view to the provided values.
      */
     public void setSystemInsets(Rect systemInsets) {
-        setPadding(systemInsets.left, systemInsets.top, systemInsets.right, systemInsets.bottom);
+        mSystemInsets.set(systemInsets.left, systemInsets.top, systemInsets.right, systemInsets.bottom);
+        requestLayout();
     }
 
     /**
@@ -142,6 +146,26 @@
     }
 
     @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        RecentsConfiguration config = Recents.getConfiguration();
+        int width = MeasureSpec.getSize(widthMeasureSpec);
+        int height = MeasureSpec.getSize(heightMeasureSpec);
+
+        // Pad the view to align the history with the stack layout
+        Rect taskStackBounds = new Rect();
+        config.getTaskStackBounds(new Rect(0, 0, width, height), mSystemInsets.top,
+                mSystemInsets.right, new Rect() /* searchBarSpaceBounds */, taskStackBounds);
+        int stackWidthPadding = (int) (config.taskStackWidthPaddingPct * taskStackBounds.width());
+        int stackHeightPadding = mContext.getResources().getDimensionPixelSize(
+                R.dimen.recents_stack_top_padding);
+        mRecyclerView.setPadding(stackWidthPadding + mSystemInsets.left,
+                stackHeightPadding + mSystemInsets.top,
+                stackWidthPadding + mSystemInsets.right, mSystemInsets.bottom);
+
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+    }
+
+    @Override
     public WindowInsets onApplyWindowInsets(WindowInsets insets) {
         setSystemInsets(insets.getSystemWindowInsets());
         return insets;
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 fb84a22..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())
@@ -431,10 +432,11 @@
                     .setStartDelay(0)
                     .setDuration(taskViewExitToAppDuration)
                     .setInterpolator(mFastOutLinearInInterpolator)
+                    .withEndAction(postAnimRunnable)
                     .start();
         } else {
             // Hide the dismiss button
-            mHeaderView.startLaunchTaskDismissAnimation();
+            mHeaderView.startLaunchTaskDismissAnimation(postAnimRunnable);
             // If this is another view in the task grouping and is in front of the launch task,
             // animate it away first
             if (occludesLaunchTarget) {
@@ -442,6 +444,7 @@
                     .translationY(getTranslationY() + taskViewAffiliateGroupEnterOffset)
                     .setStartDelay(0)
                     .setUpdateListener(null)
+                    .setListener(null)
                     .setInterpolator(mFastOutLinearInInterpolator)
                     .setDuration(taskViewExitToAppDuration)
                     .start();
@@ -463,6 +466,7 @@
             .alpha(0f)
             .setStartDelay(delay)
             .setUpdateListener(null)
+            .setListener(null)
             .setInterpolator(mFastOutSlowInInterpolator)
             .setDuration(taskViewRemoveAnimDuration)
             .withEndAction(new Runnable() {
@@ -670,21 +674,13 @@
     @Override
     public void onTaskDataLoaded() {
         SystemServicesProxy ssp = Recents.getSystemServices();
-        RecentsConfiguration config = Recents.getConfiguration();
         if (mThumbnailView != null && mHeaderView != null) {
             // Bind each of the views to the new task data
             mThumbnailView.rebindToTask(mTask);
             mHeaderView.rebindToTask(mTask);
             // Rebind any listeners
             mActionButtonView.setOnClickListener(this);
-
-            // Only enable long-click if we have a freeform workspace to drag to/from, or if we
-            // aren't already docked
-            if (ssp.hasFreeformWorkspaceSupport() || !config.hasDockedTasks) {
-                setOnLongClickListener(this);
-            } else {
-                setOnLongClickListener(null);
-            }
+            setOnLongClickListener(this);
         }
         mTaskDataLoaded = true;
     }
@@ -724,7 +720,8 @@
 
     @Override
     public boolean onLongClick(View v) {
-        if (v == this) {
+        SystemServicesProxy ssp = Recents.getSystemServices();
+        if (v == this && !ssp.hasDockedTask()) {
             // Start listening for drag events
             setClipViewInStack(false);
 
@@ -734,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/TaskViewHeader.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
index 76c6691..85b4b9b 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
@@ -284,7 +284,7 @@
     }
 
     /** Animates this task bar dismiss button when launching a task. */
-    void startLaunchTaskDismissAnimation() {
+    void startLaunchTaskDismissAnimation(final Runnable postAnimationRunanble) {
         if (mDismissButton.getVisibility() == View.VISIBLE) {
             int taskViewExitToAppDuration = mContext.getResources().getInteger(
                     R.integer.recents_task_exit_to_app_duration);
@@ -294,6 +294,7 @@
                     .setStartDelay(0)
                     .setInterpolator(mFastOutSlowInInterpolator)
                     .setDuration(taskViewExitToAppDuration)
+                    .withEndAction(postAnimationRunanble)
                     .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 723989a..3c7ff7f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -40,7 +40,6 @@
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.database.ContentObserver;
-import android.graphics.PorterDuff;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.Icon;
 import android.os.AsyncTask;
@@ -117,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;
@@ -193,6 +193,7 @@
     protected IDreamManager mDreamManager;
     PowerManager mPowerManager;
     protected StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
+    protected int mRowMinHeightLegacy;
     protected int mRowMinHeight;
     protected int mRowMaxHeight;
 
@@ -887,15 +888,6 @@
                 entry.row.setShowingLegacyBackground(true);
                 entry.legacy = true;
             }
-        } else {
-            // Using platform templates
-            final int color = sbn.getNotification().color;
-            if (isMediaNotification(entry)) {
-                entry.row.setTintColor(color == Notification.COLOR_DEFAULT
-                        ? mContext.getColor(
-                                R.color.notification_material_background_media_default_color)
-                        : color);
-            }
         }
 
         if (entry.icon != null) {
@@ -1299,6 +1291,22 @@
         }
     }
 
+    /**
+     * Called when the panel was layouted expanded for the first time after being collapsed.
+     */
+    public void onPanelExpandedAndLayouted() {
+        if (mState == StatusBarState.KEYGUARD) {
+            // 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) {
@@ -1337,7 +1345,6 @@
         PackageManager pmUser = getPackageManagerForUser(mContext,
                 entry.notification.getUser().getIdentifier());
 
-        int maxHeight = mRowMaxHeight;
         final StatusBarNotification sbn = entry.notification;
         entry.cacheContentViews(mContext, null);
 
@@ -1380,6 +1387,7 @@
                     parent, false);
             row.setExpansionLogger(this, entry.notification.getKey());
             row.setGroupManager(mGroupManager);
+            row.setOnExpandClickListener(this);
         }
 
         workAroundBadLayerDrawableOpacity(row);
@@ -1498,18 +1506,6 @@
 
             Drawable iconDrawable = StatusBarIconView.getIcon(mContext, ic);
             icon.setImageDrawable(iconDrawable);
-            if (entry.targetSdk >= Build.VERSION_CODES.LOLLIPOP
-                    || mNotificationColorUtil.isGrayscaleIcon(iconDrawable)) {
-                icon.setBackgroundResource(
-                        com.android.internal.R.drawable.notification_icon_legacy_bg);
-                int padding = mContext.getResources().getDimensionPixelSize(
-                        com.android.internal.R.dimen.notification_large_icon_circle_padding);
-                icon.setPadding(padding, padding, padding, padding);
-                if (sbn.getNotification().color != Notification.COLOR_DEFAULT) {
-                    icon.getBackground().setColorFilter(
-                            sbn.getNotification().color, PorterDuff.Mode.SRC_ATOP);
-                }
-            }
 
             if (profileBadge != null) {
                 Drawable profileDrawable = mContext.getPackageManager().getUserBadgeForDensity(
@@ -1536,11 +1532,6 @@
                         R.style.TextAppearance_Material_Notification_Parenthetical);
             }
 
-            int topPadding = Notification.Builder.calculateTopPadding(mContext,
-                    false /* hasThreeLines */,
-                    mContext.getResources().getConfiguration().fontScale);
-            title.setPadding(0, topPadding, 0, 0);
-
             contentContainerPublic.setContractedChild(publicViewLocal);
             entry.autoRedacted = true;
         }
@@ -1553,7 +1544,7 @@
             }
         }
         entry.row = row;
-        entry.row.setHeightRange(mRowMinHeight, maxHeight);
+        updateNotificationHeightRange(entry);
         entry.row.setOnActivatedListener(this);
         entry.row.setExpandable(bigContentViewLocal != null);
 
@@ -1566,11 +1557,19 @@
             row.setUserExpanded(userExpanded);
         }
         row.setUserLocked(userLocked);
-        row.setEntry(entry);
+        row.updateStatusBarNotification(entry.notification);
         applyRemoteInput(entry);
         return true;
     }
 
+    private void updateNotificationHeightRange(Entry entry) {
+        boolean customView = entry.getContentView().getId()
+                != com.android.internal.R.id.status_bar_latest_event_content;
+        boolean beforeN = entry.targetSdk < Build.VERSION_CODES.N;
+        int minHeight = customView && beforeN ? mRowMinHeightLegacy : mRowMinHeight;
+        entry.row.setHeightRange(minHeight, mRowMaxHeight);
+    }
+
     /**
      * Adds RemoteInput actions from the WearableExtender; to be removed once more apps support this
      * via first-class API.
@@ -1982,15 +1981,15 @@
     }
 
     /**
+     * @param recompute wheter the number should be recomputed
      * @return The number of notifications we show on Keyguard.
      */
-    protected abstract int getMaxKeyguardNotifications();
+    protected abstract int getMaxKeyguardNotifications(boolean recompute);
 
     /**
      * Updates expanded, dimmed and locked states of notification rows.
      */
     protected void updateRowStates() {
-        int maxKeyguardNotifications = getMaxKeyguardNotifications();
         mKeyguardIconOverflowContainer.getIconsView().removeAllViews();
 
         ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
@@ -1998,6 +1997,10 @@
 
         int visibleNotifications = 0;
         boolean onKeyguard = mState == StatusBarState.KEYGUARD;
+        int maxNotifications = 0;
+        if (onKeyguard) {
+            maxNotifications = getMaxKeyguardNotifications(true /* recompute */);
+        }
         for (int i = 0; i < N; i++) {
             NotificationData.Entry entry = activeNotifications.get(i);
             if (onKeyguard) {
@@ -2013,7 +2016,7 @@
                     == View.VISIBLE;
             boolean showOnKeyguard = shouldShowOnKeyguard(entry.notification);
             if ((isLockscreenPublicMode() && !mShowLockscreenNotifications) ||
-                    (onKeyguard && (visibleNotifications >= maxKeyguardNotifications
+                    (onKeyguard && (visibleNotifications >= maxNotifications
                             && !childWithVisibleSummary
                             || !showOnKeyguard))) {
                 entry.row.setVisibility(View.GONE);
@@ -2188,8 +2191,7 @@
         // update the contentIntent
         mNotificationClicker.register(entry.row, sbn);
 
-        entry.row.setEntry(entry);
-        entry.row.notifyContentUpdated();
+        entry.row.updateStatusBarNotification(entry.notification);
         entry.row.resetHeight();
 
         applyRemoteInput(entry);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java
index 14051916..8570198 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java
@@ -120,12 +120,12 @@
                 if (mLastHeight > mMinDragDistance) {
                     if (!mDraggedFarEnough) {
                         mDraggedFarEnough = true;
-                        mDragDownCallback.onThresholdReached();
+                        mDragDownCallback.onCrossedThreshold(true);
                     }
                 } else {
                     if (mDraggedFarEnough) {
                         mDraggedFarEnough = false;
-                        mDragDownCallback.onDragDownReset();
+                        mDragDownCallback.onCrossedThreshold(false);
                     }
                 }
                 return true;
@@ -236,7 +236,12 @@
          */
         boolean onDraggedDown(View startingChild, int dragLengthY);
         void onDragDownReset();
-        void onThresholdReached();
+
+        /**
+         * The user has dragged either above or below the threshold
+         * @param above whether he dragged above it
+         */
+        void onCrossedThreshold(boolean above);
         void onTouchSlopExceeded();
         void setEmptyDragAmount(float amount);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index 3603900..7a94a58 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -25,16 +25,17 @@
 import android.service.notification.StatusBarNotification;
 import android.util.AttributeSet;
 import android.view.MotionEvent;
+import android.view.NotificationHeaderView;
 import android.view.View;
 import android.view.ViewStub;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.animation.LinearInterpolator;
 import android.widget.Chronometer;
 import android.widget.ImageView;
+import android.widget.RemoteViews;
 
 import com.android.systemui.R;
 import com.android.systemui.classifier.FalsingManager;
-import com.android.systemui.statusbar.notification.NotificationHeaderView;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.stack.NotificationChildrenContainer;
 import com.android.systemui.statusbar.stack.StackScrollState;
@@ -89,22 +90,37 @@
     private boolean mIsHeadsUp;
     private boolean mLastChronometerRunning = true;
     private NotificationHeaderView mNotificationHeader;
-    private ViewStub mNotificationHeaderStub;
     private ViewStub mChildrenContainerStub;
     private NotificationGroupManager mGroupManager;
     private boolean mChildrenExpanded;
     private boolean mIsSummaryWithChildren;
     private NotificationChildrenContainer mChildrenContainer;
     private ViewStub mGutsStub;
-    private boolean mHasNotificationHeader;
     private boolean mIsSystemChildExpanded;
     private boolean mIsPinned;
     private FalsingManager mFalsingManager;
 
     private boolean mJustClicked;
-    private NotificationData.Entry mEntry;
+    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 {
+                boolean nowExpanded = !isExpanded();
+                setUserExpanded(nowExpanded);
+                notifyHeightChanged(true);
+                mOnExpandClickListener.onExpandClicked(ExpandableNotificationRow.this,
+                        nowExpanded);
+            }
+        }
+    };
 
     public NotificationContentView getPrivateLayout() {
         return mPrivateLayout;
@@ -117,6 +133,16 @@
     public void setIconAnimationRunning(boolean running) {
         setIconAnimationRunning(running, mPublicLayout);
         setIconAnimationRunning(running, mPrivateLayout);
+        setIconAnimationRunningForChild(running, mNotificationHeader);
+        if (mIsSummaryWithChildren) {
+            List<ExpandableNotificationRow> notificationChildren =
+                    mChildrenContainer.getNotificationChildren();
+            for (int i = 0; i < notificationChildren.size(); i++) {
+                ExpandableNotificationRow child = notificationChildren.get(i);
+                child.setIconAnimationRunning(running);
+            }
+        }
+        mIconAnimationRunning = running;
     }
 
     private void setIconAnimationRunning(boolean running, NotificationContentView layout) {
@@ -161,10 +187,17 @@
         }
     }
 
-    private void setStatusBarNotification(StatusBarNotification statusBarNotification) {
+    public void updateStatusBarNotification(StatusBarNotification statusBarNotification) {
         mStatusBarNotification = statusBarNotification;
-        mPrivateLayout.setStatusBarNotification(statusBarNotification);
+        mPrivateLayout.onNotificationUpdated(statusBarNotification);
+        mPublicLayout.onNotificationUpdated(statusBarNotification);
         updateVetoButton();
+        if (mIsSummaryWithChildren) {
+            recreateNotificationHeader();
+        }
+        if (mIconAnimationRunning) {
+            setIconAnimationRunning(true);
+        }
         onChildrenCountChanged();
     }
 
@@ -381,11 +414,6 @@
         }
     }
 
-    public void setEntry(NotificationData.Entry entry) {
-        mEntry = entry;
-        setStatusBarNotification(entry.notification);
-    }
-
     public CharSequence getSubText() {
         Notification notification = mStatusBarNotification.getNotification();
         CharSequence subText = notification.extras.getCharSequence(Notification.EXTRA_SUMMARY_TEXT);
@@ -399,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);
     }
@@ -452,6 +484,8 @@
         super.onFinishInflate();
         mPublicLayout = (NotificationContentView) findViewById(R.id.expandedPublic);
         mPrivateLayout = (NotificationContentView) findViewById(R.id.expanded);
+        mPrivateLayout.setExpandClickListener(mExpandClickListener);
+        mPublicLayout.setExpandClickListener(mExpandClickListener);
         mGutsStub = (ViewStub) findViewById(R.id.notification_guts_stub);
         mGutsStub.setOnInflateListener(new ViewStub.OnInflateListener() {
             @Override
@@ -462,15 +496,6 @@
                 mGutsStub = null;
             }
         });
-        mNotificationHeaderStub = (ViewStub) findViewById(R.id.notification_header_stub);
-        mNotificationHeaderStub.setOnInflateListener(new ViewStub.OnInflateListener() {
-            @Override
-            public void onInflate(ViewStub stub, View inflated) {
-                mNotificationHeader = (NotificationHeaderView) inflated;
-                mNotificationHeader.setGroupManager(mGroupManager);
-                mNotificationHeader.bind(mEntry);
-            }
-        });
         mChildrenContainerStub = (ViewStub) findViewById(R.id.child_container_stub);
         mChildrenContainerStub.setOnInflateListener(new ViewStub.OnInflateListener() {
 
@@ -494,6 +519,7 @@
             return;
         }
         mChildrenContainer.setVisibility(mIsSummaryWithChildren ? VISIBLE : INVISIBLE);
+        mNotificationHeader.setVisibility(mIsSummaryWithChildren ? VISIBLE : INVISIBLE);
         mPrivateLayout.setVisibility(!mIsSummaryWithChildren ? VISIBLE : INVISIBLE);
     }
 
@@ -523,6 +549,8 @@
     public void setHeightRange(int rowMinHeight, int rowMaxHeight) {
         mRowMinHeight = rowMinHeight;
         mMaxViewHeight = rowMaxHeight;
+        mPrivateLayout.setSmallHeight(mRowMinHeight);
+        mPublicLayout.setSmallHeight(mRowMinHeight);
     }
 
     public boolean isExpandable() {
@@ -534,6 +562,7 @@
 
     public void setExpandable(boolean expandable) {
         mExpandable = expandable;
+        mPrivateLayout.updateExpandButtons(isExpandable());
     }
 
     /**
@@ -654,8 +683,7 @@
         if (mSensitive && mHideSensitiveForIntrinsicHeight) {
             return mRowMinHeight;
         } else if (mIsSummaryWithChildren && !mOnKeyguard) {
-            return mChildrenContainer.getIntrinsicHeight()
-                    + mNotificationHeader.getHeight();
+            return mChildrenContainer.getIntrinsicHeight();
         } else if (mIsHeadsUp) {
             if (inExpansionState) {
                 return Math.max(mMaxExpandHeight, mHeadsUpHeight);
@@ -681,12 +709,18 @@
     }
 
     private void onChildrenCountChanged() {
-        mIsSummaryWithChildren  = BaseStatusBar.ENABLE_CHILD_NOTIFICATIONS
+        mIsSummaryWithChildren = BaseStatusBar.ENABLE_CHILD_NOTIFICATIONS
                 && mGroupManager.hasGroupChildren(mStatusBarNotification);
-        if (mIsSummaryWithChildren && mChildrenContainer == null) {
-            mChildrenContainerStub.inflate();
+        if (mIsSummaryWithChildren) {
+            if (mChildrenContainer == null) {
+                mChildrenContainerStub.inflate();
+            }
+            if (mNotificationHeader == null) {
+                recreateNotificationHeader();
+            }
         }
-        updateNotificationHeader();
+        mPrivateLayout.updateExpandButtons(isExpandable());
+        updateHeaderChildCount();
         updateChildrenVisibility(true);
     }
 
@@ -771,6 +805,7 @@
             animateShowingPublic(delay, duration);
         }
 
+        mPrivateLayout.updateExpandButtons(isExpandable());
         updateVetoButton();
         mShowingPublicInitialized = true;
     }
@@ -811,23 +846,11 @@
         }
     }
 
-    public void updateNotificationHeader() {
-        boolean hasHeader = hasNotificationHeader();
-        if (hasHeader != mHasNotificationHeader) {
-            if (hasHeader) {
-                if (mNotificationHeader == null) {
-                    mNotificationHeaderStub.inflate();
-                }
-                mNotificationHeader.setVisibility(View.VISIBLE);
-            } else if (mNotificationHeader != null) {
-                mNotificationHeader.setVisibility(View.GONE);
-            }
-            notifyHeightChanged(true  /* needsAnimation */);
+    public void updateHeaderChildCount() {
+        if (mIsSummaryWithChildren) {
+            mNotificationHeader.setChildCount(
+                    mChildrenContainer.getNotificationChildren().size());
         }
-        if (hasHeader) {
-            mNotificationHeader.bind(mEntry);
-        }
-        mHasNotificationHeader = hasHeader;
     }
 
     public static void applyTint(View v, int color) {
@@ -876,8 +899,7 @@
     @Override
     public int getMaxContentHeight() {
         if (mIsSummaryWithChildren && !mShowingPublic) {
-            return mChildrenContainer.getMaxContentHeight()
-                    + mNotificationHeader.getHeight();
+            return mChildrenContainer.getMaxContentHeight();
         }
         NotificationContentView showingLayout = getShowingLayout();
         return showingLayout.getMaxHeight();
@@ -885,15 +907,19 @@
 
     @Override
     public int getMinHeight() {
-        if (mIsSummaryWithChildren && !mOnKeyguard) {
-            return mChildrenContainer.getMinHeight()
-                    + mNotificationHeader.getHeight();
-        }
         NotificationContentView showingLayout = getShowingLayout();
         return showingLayout.getMinHeight();
     }
 
     @Override
+    public int getMinExpandHeight() {
+        if (mIsSummaryWithChildren && !mOnKeyguard) {
+            return mChildrenContainer.getMinHeight();
+        }
+        return getMinHeight();
+    }
+
+    @Override
     protected boolean shouldLimitViewHeight() {
         return !mIsSummaryWithChildren;
     }
@@ -908,9 +934,21 @@
         }
     }
 
-    public void notifyContentUpdated() {
-        mPublicLayout.notifyContentUpdated();
-        mPrivateLayout.notifyContentUpdated();
+    private void recreateNotificationHeader() {
+        final Notification.Builder builder = Notification.Builder.recoverBuilder(getContext(),
+                getStatusBarNotification().getNotification());
+        final RemoteViews header = builder.makeNotificationHeader();
+        if (mNotificationHeader == null) {
+            mNotificationHeader = (NotificationHeaderView) header.apply(getContext(), this);
+            final View expandButton = mNotificationHeader.findViewById(
+                    com.android.internal.R.id.expand_button);
+            expandButton.setVisibility(VISIBLE);
+            mNotificationHeader.setOnClickListener(mExpandClickListener);
+            addView(mNotificationHeader);
+        } else {
+            header.reapply(getContext(), mNotificationHeader);
+        }
+        updateHeaderChildCount();
     }
 
     public boolean isMaxExpandHeightInitialized() {
@@ -958,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/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
index af59ac7..51602e7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
@@ -195,6 +195,15 @@
     }
 
     /**
+     * @return The minimum height this child chan be expanded to. Note that this might be different
+     * than {@link #getMinHeight()} because some elements can't be collapsed by an expand gesture
+     * to it's absolute minimal height
+     */
+    public int getMinExpandHeight() {
+        return getHeight();
+    }
+
+    /**
      * Sets the notification as dimmed. The default implementation does nothing.
      *
      * @param dimmed Whether the notification should be dimmed.
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/NotificationBigMediaNarrowViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBigMediaNarrowViewWrapper.java
deleted file mode 100644
index 91e5404..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBigMediaNarrowViewWrapper.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * 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.systemui.statusbar;
-
-import android.content.Context;
-import android.view.View;
-
-/**
- * Wraps a big media narrow notification template layout.
- */
-public class NotificationBigMediaNarrowViewWrapper extends NotificationMediaViewWrapper {
-
-    protected NotificationBigMediaNarrowViewWrapper(Context ctx,
-            View view) {
-        super(ctx, view);
-    }
-
-    @Override
-    public boolean needsRoundRectClipping() {
-        return true;
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBigPictureViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBigPictureViewWrapper.java
deleted file mode 100644
index ffe0cd1..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBigPictureViewWrapper.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * 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.systemui.statusbar;
-
-import android.content.Context;
-import android.view.View;
-
-/**
- * Wraps a notification view inflated from a big picture style template.
- */
-public class NotificationBigPictureViewWrapper extends NotificationTemplateViewWrapper {
-
-    protected NotificationBigPictureViewWrapper(Context ctx, View view) {
-        super(ctx, view);
-    }
-
-    @Override
-    public boolean needsRoundRectClipping() {
-        return true;
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
index 5aedaf1..fb8086c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
@@ -24,6 +24,7 @@
 import android.graphics.Rect;
 import android.service.notification.StatusBarNotification;
 import android.util.AttributeSet;
+import android.view.NotificationHeaderView;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewOutlineProvider;
@@ -52,7 +53,6 @@
 
     private final Rect mClipBounds = new Rect();
     private final int mSingleLineHeight;
-    private final int mSmallHeight;
     private final int mHeadsUpHeight;
     private final int mRoundRectRadius;
     private final Interpolator mLinearInterpolator = new LinearInterpolator();
@@ -77,7 +77,9 @@
     private boolean mIsHeadsUp;
     private boolean mShowingLegacyBackground;
     private boolean mIsChildInGroup;
+    private int mSmallHeight;
     private StatusBarNotification mStatusBarNotification;
+    private NotificationGroupManager mGroupManager;
 
     private final ViewTreeObserver.OnPreDrawListener mEnableAnimationPredrawListener
             = new ViewTreeObserver.OnPreDrawListener() {
@@ -96,7 +98,7 @@
                     mRoundRectRadius);
         }
     };
-    private NotificationGroupManager mGroupManager;
+    private OnClickListener mExpandClickListener;
 
     public NotificationContentView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -104,7 +106,6 @@
         mFadePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.ADD));
         mSingleLineHeight = getResources().getDimensionPixelSize(
                 R.dimen.notification_single_line_height);
-        mSmallHeight = getResources().getDimensionPixelSize(R.dimen.notification_min_height);
         mHeadsUpHeight = getResources().getDimensionPixelSize(R.dimen.notification_mid_height);
         mRoundRectRadius = getResources().getDimensionPixelSize(
                 R.dimen.notification_material_rounded_rect_radius);
@@ -114,6 +115,10 @@
         setOutlineProvider(mOutlineProvider);
     }
 
+    public void setSmallHeight(int smallHeight) {
+        mSmallHeight = smallHeight;
+    }
+
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         int heightMode = MeasureSpec.getMode(heightMeasureSpec);
@@ -426,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;
@@ -443,19 +453,6 @@
         }
     }
 
-    public void notifyContentUpdated() {
-        updateSingleLineView();
-        selectLayout(false /* animate */, true /* force */);
-        if (mContractedChild != null) {
-            mContractedWrapper.notifyContentUpdated();
-            mContractedWrapper.setDark(mDark, false /* animate */, 0 /* delay */);
-        }
-        if (mExpandedChild != null) {
-            mExpandedWrapper.notifyContentUpdated();
-        }
-        updateRoundRectClipping();
-    }
-
     public boolean isContentExpandable() {
         return mExpandedChild != null;
     }
@@ -488,9 +485,21 @@
         updateSingleLineView();
     }
 
-    public void setStatusBarNotification(StatusBarNotification statusBarNotification) {
+    public void onNotificationUpdated(StatusBarNotification statusBarNotification) {
         mStatusBarNotification = statusBarNotification;
         updateSingleLineView();
+        selectLayout(false /* animate */, true /* force */);
+        if (mContractedChild != null) {
+            mContractedWrapper.notifyContentUpdated();
+            mContractedWrapper.setDark(mDark, false /* animate */, 0 /* delay */);
+        }
+        if (mExpandedChild != null) {
+            mExpandedWrapper.notifyContentUpdated();
+        }
+        if (mHeadsUpChild != null) {
+            mHeadsUpWrapper.notifyContentUpdated();
+        }
+        updateRoundRectClipping();
     }
 
     private void updateSingleLineView() {
@@ -515,4 +524,20 @@
     public void setGroupManager(NotificationGroupManager groupManager) {
         mGroupManager = groupManager;
     }
+
+    public void setExpandClickListener(OnClickListener expandClickListener) {
+        mExpandClickListener = expandClickListener;
+    }
+
+    public void updateExpandButtons(boolean expandable) {
+        if (mExpandedChild != null) {
+            mExpandedWrapper.updateExpandability(expandable, mExpandClickListener);
+        }
+        if (mContractedChild != null) {
+            mContractedWrapper.updateExpandability(expandable, mExpandClickListener);
+        }
+        if (mHeadsUpChild != null) {
+            mHeadsUpWrapper.updateExpandability(expandable,  mExpandClickListener);
+        }
+    }
 }
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/NotificationMediaViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaViewWrapper.java
deleted file mode 100644
index 953c373..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaViewWrapper.java
+++ /dev/null
@@ -1,37 +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 com.android.systemui.statusbar;
-
-import android.content.Context;
-import android.view.View;
-
-/**
- * Wraps a media notification.
- */
-public class NotificationMediaViewWrapper extends NotificationTemplateViewWrapper {
-
-    protected NotificationMediaViewWrapper(Context ctx, View view) {
-        super(ctx, view);
-    }
-
-    @Override
-    public void setDark(boolean dark, boolean fade, long delay) {
-
-        // Only update the large icon, because the rest is already inverted.
-        setPictureGrayscale(dark, fade, delay);
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationTemplateViewWrapper.java
index af6ccd8..f20ccd5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationTemplateViewWrapper.java
@@ -20,24 +20,32 @@
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
 import android.content.Context;
+import android.content.res.ColorStateList;
 import android.graphics.Color;
 import android.graphics.ColorFilter;
 import android.graphics.ColorMatrix;
 import android.graphics.ColorMatrixColorFilter;
 import android.graphics.PorterDuff;
 import android.graphics.PorterDuffColorFilter;
+import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.text.TextUtils;
+import android.view.MotionEvent;
 import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.ViewGroup;
 import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
 import android.widget.ImageView;
+import android.widget.ProgressBar;
 import android.widget.TextView;
 
 import com.android.systemui.R;
 import com.android.systemui.ViewInvertHelper;
 import com.android.systemui.statusbar.phone.NotificationPanelView;
 
+import java.util.ArrayList;
+
 /**
  * Wraps a notification view inflated from a template.
  */
@@ -47,69 +55,64 @@
     private final PorterDuffColorFilter mIconColorFilter = new PorterDuffColorFilter(
             0, PorterDuff.Mode.SRC_ATOP);
     private final int mIconDarkAlpha;
-    private final int mIconBackgroundDarkColor;
+    private final int mIconDarkColor = 0xffffffff;
+    private final int mDarkProgressTint = 0xffffffff;
     private final Interpolator mLinearOutSlowInInterpolator;
 
-    private int mIconBackgroundColor;
+    private int mColor;
     private ViewInvertHelper mInvertHelper;
     private ImageView mIcon;
     protected ImageView mPicture;
 
-    /** Whether the icon needs to be forced grayscale when in dark mode. */
-    private boolean mIconForceGraysaleWhenDark;
     private TextView mSubText;
-    private TextView mInfoText;
-    private View mProfileBadge;
-    private View mThirdLineDivider;
-    private View mThirdLine;
+    private View mSubTextDivider;
+    private ImageView mExpandButton;
+    private ViewGroup mNotificationHeader;
+    private ProgressBar mProgressBar;
 
     protected NotificationTemplateViewWrapper(Context ctx, View view) {
         super(view);
         mIconDarkAlpha = ctx.getResources().getInteger(R.integer.doze_small_icon_alpha);
-        mIconBackgroundDarkColor =
-                ctx.getColor(R.color.doze_small_icon_background_color);
         mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(ctx,
                 android.R.interpolator.linear_out_slow_in);
+
         resolveViews();
     }
 
     private void resolveViews() {
         View mainColumn = mView.findViewById(com.android.internal.R.id.notification_main_column);
-        mInvertHelper = mainColumn != null
-                ? new ViewInvertHelper(mainColumn, NotificationPanelView.DOZE_ANIMATION_DURATION)
-                : null;
-        ImageView largeIcon = (ImageView) mView.findViewById(com.android.internal.R.id.icon);
-        ImageView rightIcon = (ImageView) mView.findViewById(com.android.internal.R.id.right_icon);
-        mIcon = resolveIcon(largeIcon, rightIcon);
-        mPicture = resolvePicture(largeIcon);
-        mIconBackgroundColor = resolveBackgroundColor(mIcon);
-        mSubText = (TextView) mView.findViewById(com.android.internal.R.id.text);
-        mInfoText = (TextView) mView.findViewById(com.android.internal.R.id.info);
-        mProfileBadge = mView.findViewById(com.android.internal.R.id.profile_badge_line3);
-        mThirdLineDivider = mView.findViewById(com.android.internal.R.id.overflow_divider);
-        mThirdLine = mView.findViewById(com.android.internal.R.id.line3);
-
-        // If the icon already has a color filter, we assume that we already forced the icon to be
-        // white when we created the notification.
-        final Drawable iconDrawable = mIcon != null ? mIcon.getDrawable() : null;
-        mIconForceGraysaleWhenDark = iconDrawable != null && iconDrawable.getColorFilter() != null;
+        mIcon = (ImageView) mView.findViewById(com.android.internal.R.id.icon);
+        mPicture = (ImageView) mView.findViewById(com.android.internal.R.id.right_icon);
+        mSubText = (TextView) mView.findViewById(com.android.internal.R.id.header_sub_text);
+        mSubTextDivider = mView.findViewById(com.android.internal.R.id.sub_text_divider);
+        mExpandButton = (ImageView) mView.findViewById(com.android.internal.R.id.expand_button);
+        mColor = resolveColor(mExpandButton);
+        final View progress = mView.findViewById(com.android.internal.R.id.progress);
+        if (progress instanceof ProgressBar) {
+            mProgressBar = (ProgressBar) progress;
+        } else {
+            // It's still a viewstub
+            mProgressBar = null;
+        }
+        mNotificationHeader = (ViewGroup) mView.findViewById(
+                com.android.internal.R.id.notification_header);
+        ArrayList<View> viewsToInvert = new ArrayList<>();
+        if (mainColumn != null) {
+            viewsToInvert.add(mainColumn);
+        }
+        for (int i = 0; i < mNotificationHeader.getChildCount(); i++) {
+            View child = mNotificationHeader.getChildAt(i);
+            if (child != mIcon) {
+                viewsToInvert.add(child);
+            }
+        }
+        mInvertHelper = new ViewInvertHelper(viewsToInvert,
+                NotificationPanelView.DOZE_ANIMATION_DURATION);
     }
 
-    private ImageView resolveIcon(ImageView largeIcon, ImageView rightIcon) {
-        return largeIcon != null && largeIcon.getBackground() != null ? largeIcon
-                : rightIcon != null && rightIcon.getVisibility() == View.VISIBLE ? rightIcon
-                : null;
-    }
-
-    private ImageView resolvePicture(ImageView largeIcon) {
-        return largeIcon != null && largeIcon.getBackground() == null
-                ? largeIcon
-                : null;
-    }
-
-    private int resolveBackgroundColor(ImageView icon) {
-        if (icon != null && icon.getBackground() != null) {
-            ColorFilter filter = icon.getBackground().getColorFilter();
+    private int resolveColor(ImageView icon) {
+        if (icon != null && icon.getDrawable() != null) {
+            ColorFilter filter = icon.getDrawable().getColorFilter();
             if (filter instanceof PorterDuffColorFilter) {
                 return ((PorterDuffColorFilter) filter).getColor();
             }
@@ -138,18 +141,43 @@
             if (fade) {
                 fadeIconColorFilter(mIcon, dark, delay);
                 fadeIconAlpha(mIcon, dark, delay);
-                if (!mIconForceGraysaleWhenDark) {
-                    fadeGrayscale(mIcon, dark, delay);
-                }
             } else {
                 updateIconColorFilter(mIcon, dark);
                 updateIconAlpha(mIcon, dark);
-                if (!mIconForceGraysaleWhenDark) {
-                    updateGrayscale(mIcon, dark);
-                }
             }
         }
         setPictureGrayscale(dark, fade, delay);
+        setProgressBarDark(dark, fade, delay);
+    }
+
+    private void setProgressBarDark(boolean dark, boolean fade, long delay) {
+        if (mProgressBar != null) {
+            if (fade) {
+                fadeProgressDark(mProgressBar, dark, delay);
+            } else {
+                updateProgressDark(mProgressBar, dark);
+            }
+        }
+    }
+
+    private void fadeProgressDark(final ProgressBar target, final boolean dark, long delay) {
+        startIntensityAnimation(new ValueAnimator.AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator animation) {
+                float t = (float) animation.getAnimatedValue();
+                updateProgressDark(target, t);
+            }
+        }, dark, delay, null /* listener */);
+    }
+
+    private void updateProgressDark(ProgressBar target, float intensity) {
+        int color = interpolateColor(mColor, mDarkProgressTint, intensity);
+        target.getIndeterminateDrawable().mutate().setTint(color);
+        target.getProgressDrawable().mutate().setTint(color);
+    }
+
+    private void updateProgressDark(ProgressBar target, boolean dark) {
+        updateProgressDark(target, dark ? 1f : 0f);
     }
 
     protected void setPictureGrayscale(boolean grayscale, boolean fade, long delay) {
@@ -218,14 +246,14 @@
     }
 
     private void updateIconColorFilter(ImageView target, float intensity) {
-        int color = interpolateColor(mIconBackgroundColor, mIconBackgroundDarkColor, intensity);
+        int color = interpolateColor(mColor, mIconDarkColor, intensity);
         mIconColorFilter.setColor(color);
-        Drawable background = target.getBackground();
+        Drawable iconDrawable = target.getDrawable();
 
-        // The background might be null for legacy notifications. Also, the notification might have
-        // been modified during the animation, so background might be null here.
-        if (background != null) {
-            background.mutate().setColorFilter(mIconColorFilter);
+        // Also, the notification might have been modified during the animation, so background
+        // might be null here.
+        if (iconDrawable != null) {
+            iconDrawable.mutate().setColorFilter(mIconColorFilter);
         }
     }
 
@@ -250,33 +278,17 @@
         boolean subTextAvailable = !TextUtils.isEmpty(mSubText.getText());
         if (visible && subTextAvailable) {
             mSubText.setVisibility(View.VISIBLE);
+            mSubTextDivider.setVisibility(View.VISIBLE);
         } else {
             mSubText.setVisibility(View.GONE);
+            mSubTextDivider.setVisibility(View.GONE);
         }
-        // TODO: figure out what to do with the number (same place as contentInfo)
-        // work profile badge. For now we hide it since it looks nicer.
-        boolean infoAvailable = !TextUtils.isEmpty(mInfoText.getText());
-        if (visible && infoAvailable) {
-            mInfoText.setVisibility(View.VISIBLE);
-        } else {
-            mInfoText.setVisibility(View.GONE);
-        }
-        boolean showThirdLine = (visible && (infoAvailable || subTextAvailable))
-                || mProfileBadge.getVisibility() == View.VISIBLE;
-        if (mThirdLineDivider != null) {
-            if (showThirdLine) {
-                mThirdLineDivider.setVisibility(View.VISIBLE);
-            } else {
-                mThirdLineDivider.setVisibility(View.GONE);
-            }
-        }
-        if (mThirdLine != null) {
-            if (showThirdLine) {
-                mThirdLine.setVisibility(View.VISIBLE);
-            } else {
-                mThirdLine.setVisibility(View.GONE);
-            }
-        }
+    }
+
+    @Override
+    public void updateExpandability(boolean expandable, View.OnClickListener onClickListener) {
+        mExpandButton.setVisibility(expandable ? View.VISIBLE : View.GONE);
+        mNotificationHeader.setOnClickListener(expandable ? onClickListener : null);
     }
 
     private void updateGrayscaleMatrix(float intensity) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewWrapper.java
index 9bce548..e83ecb7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewWrapper.java
@@ -34,15 +34,7 @@
 
     public static NotificationViewWrapper wrap(Context ctx, View v) {
         if (v.getId() == com.android.internal.R.id.status_bar_latest_event_content) {
-            if (TAG_BIG_MEDIA_NARROW.equals(v.getTag())) {
-                return new NotificationBigMediaNarrowViewWrapper(ctx, v);
-            } else if (TAG_MEDIA.equals(v.getTag())) {
-                return new NotificationMediaViewWrapper(ctx, v);
-            } else if (TAG_BIG_PICTURE.equals(v.getTag())) {
-                return new NotificationBigMediaNarrowViewWrapper(ctx, v);
-            } else {
-                return new NotificationTemplateViewWrapper(ctx, v);
-            }
+            return new NotificationTemplateViewWrapper(ctx, v);
         } else {
             return new NotificationCustomViewWrapper(v);
         }
@@ -83,4 +75,12 @@
     public void setSubTextVisible(boolean visible) {
         mSubTextVisible = visible;
     }
+
+    /**
+     * Update the appearance of the expand button.
+     *
+     * @param expandable should this view be expandable
+     * @param onClickListener the listener to invoke when the expand affordance is clicked on
+     */
+    public void updateExpandability(boolean expandable, View.OnClickListener onClickListener) {}
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderView.java
deleted file mode 100644
index ec26cc4..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderView.java
+++ /dev/null
@@ -1,209 +0,0 @@
-/*
- * 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.systemui.statusbar.notification;
-
-import android.annotation.Nullable;
-import android.app.Notification;
-import android.content.Context;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.graphics.drawable.Drawable;
-import android.service.notification.StatusBarNotification;
-import android.text.TextUtils;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.FrameLayout;
-import android.widget.ImageButton;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import com.android.internal.util.NotificationColorUtil;
-import com.android.systemui.R;
-import com.android.systemui.statusbar.BaseStatusBar;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
-import com.android.systemui.statusbar.NotificationData;
-import com.android.systemui.statusbar.phone.NotificationGroupManager;
-
-import java.util.List;
-
-/**
- * A header for a notification view
- */
-public class NotificationHeaderView extends FrameLayout {
-
-    private static final int DEFAULT_ICON_TINT_COLOR = 0xff616161;
-    private final NotificationColorUtil mNotificationColorUtil;
-    private NotificationData.Entry mNotificationEntry;
-    private ImageView mIconView;
-    private TextView mAppName;
-    private TextView mPostTime;
-    private TextView mChildCount;
-    private TextView mSubTextDivider;
-    private TextView mSubText;
-    private NotificationGroupManager mGroupManager;
-    private ImageButton mExpandButton;
-
-    public NotificationHeaderView(Context context) {
-        this(context, null);
-    }
-
-    public NotificationHeaderView(Context context, @Nullable AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public NotificationHeaderView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
-        this(context, attrs, defStyleAttr, 0);
-    }
-
-    public NotificationHeaderView(Context context, @Nullable AttributeSet attrs, int defStyleAttr,
-            int defStyleRes) {
-        super(context, attrs, defStyleAttr, defStyleRes);
-        mNotificationColorUtil = NotificationColorUtil.getInstance(context);
-    }
-
-    @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-        mIconView = (ImageView) findViewById(R.id.header_notification_icon);
-        mAppName = (TextView) findViewById(R.id.app_name_text);
-        mSubTextDivider = (TextView) findViewById(R.id.app_title_sub_text_divider);
-        mSubText = (TextView) findViewById(R.id.title_sub_text);
-        mPostTime = (TextView) findViewById(R.id.post_time);
-        mChildCount = (TextView) findViewById(R.id.number_of_children);
-        mExpandButton = (ImageButton) findViewById(R.id.notification_expand_button);
-        mExpandButton.setOnClickListener(new OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                mGroupManager.toggleGroupExpansion(mNotificationEntry.notification);
-            }
-        });
-    }
-
-    public void bind(NotificationData.Entry notificationEntry) {
-        mNotificationEntry = notificationEntry;
-        StatusBarNotification sbn = notificationEntry.notification;
-        int notificationColor = getNotificationColor(sbn);
-        bindIcon(notificationColor);
-        bindNumber(notificationColor);
-        bindAppName(sbn);
-        bindSubText();
-        bindTime(sbn);
-        bindExpandButton(sbn);
-    }
-
-    private void bindExpandButton(StatusBarNotification sbn) {
-        boolean summaryOfGroup = mGroupManager.isSummaryOfGroup(sbn);
-        mExpandButton.setVisibility(summaryOfGroup ? VISIBLE : GONE);
-    }
-
-    private void bindSubText() {
-        List<ExpandableNotificationRow> notificationChildren =
-                mNotificationEntry.row.getNotificationChildren();
-        CharSequence subText = null;
-        if (notificationChildren != null) {
-            for (int i = 0; i < notificationChildren.size(); i++) {
-                ExpandableNotificationRow row = notificationChildren.get(i);
-                CharSequence rowSubText = row.getSubText();
-                if (TextUtils.isEmpty(rowSubText)
-                        || (subText != null && !subText.equals(rowSubText))) {
-                    // The children don't have a common subText
-                    subText = null;
-                    break;
-                } else if (subText == null) {
-                    subText = rowSubText;
-                }
-            }
-        };
-        setSubText(subText);
-    }
-
-    private void setSubText(CharSequence subText) {
-        boolean goneInHeader = TextUtils.isEmpty(subText);
-        if (goneInHeader) {
-            mSubText.setVisibility(GONE);
-            mSubTextDivider.setVisibility(GONE);
-        } else {
-            mSubText.setVisibility(VISIBLE);
-            mSubText.setText(subText);
-            mSubTextDivider.setVisibility(VISIBLE);
-        }
-        List<ExpandableNotificationRow> notificationChildren =
-                mNotificationEntry.row.getNotificationChildren();
-        if (notificationChildren != null) {
-            for (int i = 0; i < notificationChildren.size(); i++) {
-                ExpandableNotificationRow row = notificationChildren.get(i);
-                row.setContentSubTextVisible(goneInHeader);
-            }
-        }
-    }
-
-    private int getNotificationColor(StatusBarNotification sbn) {
-        int color = sbn.getNotification().color;
-        if (color == Notification.COLOR_DEFAULT) {
-            return DEFAULT_ICON_TINT_COLOR;
-        }
-        return color;
-    }
-
-    private void bindNumber(int notificationColor) {
-        int numberOfNotificationChildren = mNotificationEntry.row.getNumberOfNotificationChildren();
-        boolean visible = numberOfNotificationChildren > 0;
-        if (visible) {
-            mChildCount.setText("(" + numberOfNotificationChildren + ")");
-            mChildCount.setTextColor(notificationColor);
-            mChildCount.setVisibility(VISIBLE);
-        } else {
-            mChildCount.setVisibility(GONE);
-        }
-    }
-
-    private void bindTime(StatusBarNotification sbn) {
-
-    }
-
-    private void bindIcon(int notificationColor) {
-        Drawable icon = mNotificationEntry.icon.getDrawable().getConstantState()
-                .newDrawable(getResources()).mutate();
-        mIconView.setImageDrawable(icon);
-        if (NotificationUtils.isGrayscale(mIconView, mNotificationColorUtil)) {
-            icon.setTint(notificationColor);
-        }
-    }
-
-    private void bindAppName(StatusBarNotification sbn) {
-        PackageManager pmUser = BaseStatusBar.getPackageManagerForUser(getContext(),
-                sbn.getUser().getIdentifier());
-        final String pkg = sbn.getPackageName();
-        String appname = pkg;
-        try {
-            final ApplicationInfo info = pmUser.getApplicationInfo(pkg,
-                    PackageManager.GET_UNINSTALLED_PACKAGES
-                            | PackageManager.GET_DISABLED_COMPONENTS);
-            if (info != null) {
-                appname = String.valueOf(pmUser.getApplicationLabel(info));
-
-            }
-        } catch (PackageManager.NameNotFoundException e) {
-            // app is gone, just show package name
-        }
-        mAppName.setText(appname);
-    }
-
-    public void setGroupManager(NotificationGroupManager groupManager) {
-        mGroupManager = groupManager;
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
index a15d35e..784cb48 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -96,6 +96,11 @@
         mEmptyDragAmount = emptyDragAmount;
     }
 
+    public float getMinStackScrollerPadding(int height, int keyguardStatusHeight) {
+        return mClockYFractionMin * height + keyguardStatusHeight / 2
+                + mClockNotificationsMarginMin;
+    }
+
     public void run(Result result) {
         int y = getClockY() - mKeyguardStatusHeight / 2;
         float clockAdjustment = getClockYExpansionAdjustment();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
index fbe9730..08da0d3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
@@ -92,8 +92,6 @@
         if (group.children.isEmpty()) {
             if (group.summary == null) {
                 mGroupMap.remove(groupKey);
-            } else if (!group.expanded) {
-                group.summary.row.updateNotificationHeader();
             }
         }
     }
@@ -109,9 +107,6 @@
         }
         if (notif.isGroupChild()) {
             group.children.add(added);
-            if (group.summary != null && group.children.size() == 1 && !group.expanded) {
-                group.summary.row.updateNotificationHeader();
-            }
         } else {
             group.summary = added;
             group.expanded = added.row.areChildrenExpanded();
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 79701ed..73ee363 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -44,6 +44,7 @@
 import android.view.animation.PathInterpolator;
 import android.widget.FrameLayout;
 import android.widget.TextView;
+
 import com.android.internal.logging.MetricsLogger;
 import com.android.keyguard.KeyguardStatusView;
 import com.android.systemui.DejankUtils;
@@ -448,6 +449,36 @@
         requestScrollerTopPaddingUpdate(animate);
     }
 
+    /**
+     * @param maximum the maximum to return at most
+     * @return the maximum keyguard notifications that can fit on the screen
+     */
+    public int computeMaxKeyguardNotifications(int maximum) {
+        float minPadding = mClockPositionAlgorithm.getMinStackScrollerPadding(getHeight(),
+                mKeyguardStatusView.getHeight());
+        int keyguardPadding = getResources().getDimensionPixelSize(
+                R.dimen.notification_padding_dimmed);
+        final int overflowheight = getResources().getDimensionPixelSize(
+                R.dimen.notification_summary_height);
+        float bottomStackSize = mNotificationStackScroller.getKeyguardBottomStackSize();
+        float availableSpace = mNotificationStackScroller.getHeight() - minPadding - overflowheight
+                - bottomStackSize;
+        int count = 0;
+        for (int i = 0; i < mNotificationStackScroller.getChildCount(); i++) {
+            ExpandableView child = (ExpandableView) mNotificationStackScroller.getChildAt(i);
+            if (!(child instanceof ExpandableNotificationRow)) {
+                continue;
+            }
+            availableSpace -= child.getMinHeight() + keyguardPadding;
+            if (availableSpace >= 0 && count < maximum) {
+                count++;
+            } else {
+                return count;
+            }
+        }
+        return count;
+    }
+
     private void startClockAnimation(int y) {
         if (mClockAnimationTarget == y) {
             return;
@@ -924,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;
@@ -1663,7 +1686,7 @@
     }
 
     private float getFadeoutAlpha() {
-        float alpha = (getNotificationsTopY() + mNotificationStackScroller.getItemHeight())
+        float alpha = (getNotificationsTopY() + mNotificationStackScroller.getFirstItemMinHeight())
                 / (mQsMinExpansionHeight + mNotificationStackScroller.getBottomStackPeekSize()
                 - mNotificationStackScroller.getCollapseSecondCardPadding());
         alpha = Math.max(0, Math.min(alpha, 1));
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index fafedc3..21d803d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -36,9 +36,7 @@
 import com.android.systemui.EventLogConstants;
 import com.android.systemui.EventLogTags;
 import com.android.systemui.R;
-import com.android.systemui.classifier.Classifier;
 import com.android.systemui.classifier.FalsingManager;
-import com.android.systemui.classifier.HumanInteractionClassifier;
 import com.android.systemui.doze.DozeLog;
 import com.android.systemui.statusbar.FlingAnimationUtils;
 import com.android.systemui.statusbar.StatusBarState;
@@ -891,6 +889,7 @@
                         if (mStatusBar.getStatusBarWindow().getHeight()
                                 != mStatusBar.getStatusBarHeight()) {
                             getViewTreeObserver().removeOnGlobalLayoutListener(this);
+                            mStatusBar.onPanelExpandedAndLayouted();
                             if (animate) {
                                 mBar.startOpeningPanel(PanelView.this);
                                 notifyExpandingStarted();
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 6d8e650..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;
@@ -110,8 +109,8 @@
 import com.android.systemui.EventLogTags;
 import com.android.systemui.Prefs;
 import com.android.systemui.R;
-import com.android.systemui.classifier.FalsingManager;
 import com.android.systemui.assist.AssistManager;
+import com.android.systemui.classifier.FalsingManager;
 import com.android.systemui.doze.DozeHost;
 import com.android.systemui.doze.DozeLog;
 import com.android.systemui.keyguard.KeyguardViewMediator;
@@ -171,7 +170,6 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.TreeSet;
@@ -338,7 +336,7 @@
     private long mKeyguardFadingAwayDelay;
     private long mKeyguardFadingAwayDuration;
 
-    int mKeyguardMaxNotificationCount;
+    int mMaxAllowedKeyguardNotifications;
 
     boolean mExpandedVisible;
 
@@ -428,6 +426,7 @@
     private boolean mAutohideSuspended;
     private int mStatusBarMode;
     private int mNavigationBarMode;
+    private int mMaxKeyguardNotifications;
 
     private ViewMediatorCallback mKeyguardViewMediatorCallback;
     private ScrimController mScrimController;
@@ -3129,10 +3128,11 @@
         mNaturalBarHeight = res.getDimensionPixelSize(
                 com.android.internal.R.dimen.status_bar_height);
 
+        mRowMinHeightLegacy =  res.getDimensionPixelSize(R.dimen.notification_min_height_legacy);
         mRowMinHeight =  res.getDimensionPixelSize(R.dimen.notification_min_height);
         mRowMaxHeight =  res.getDimensionPixelSize(R.dimen.notification_max_height);
 
-        mKeyguardMaxNotificationCount = res.getInteger(R.integer.keyguard_max_notification_count);
+        mMaxAllowedKeyguardNotifications = res.getInteger(R.integer.keyguard_max_notification_count);
 
         if (DEBUG) Log.v(TAG, "updateResources");
     }
@@ -3913,8 +3913,18 @@
     }
 
     @Override
-    protected int getMaxKeyguardNotifications() {
-        return mKeyguardMaxNotificationCount;
+    protected int getMaxKeyguardNotifications(boolean recompute) {
+        if (recompute) {
+            mMaxKeyguardNotifications = Math.max(1,
+                    mNotificationPanel.computeMaxKeyguardNotifications(
+                            mMaxAllowedKeyguardNotifications));
+            return mMaxKeyguardNotifications;
+        }
+        return mMaxKeyguardNotifications;
+    }
+
+    public int getMaxKeyguardNotifications() {
+        return getMaxKeyguardNotifications(false /* recompute */);
     }
 
     public NavigationBarView getNavigationBarView() {
@@ -3944,11 +3954,12 @@
     @Override
     public void onDragDownReset() {
         mStackScroller.setDimmed(true /* dimmed */, true /* animated */);
+        mStackScroller.resetScrollPosition();
     }
 
     @Override
-    public void onThresholdReached() {
-        mStackScroller.setDimmed(false /* dimmed */, true /* animate */);
+    public void onCrossedThreshold(boolean above) {
+        mStackScroller.setDimmed(!above /* dimmed */, true /* animate */);
     }
 
     @Override
@@ -3987,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/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
index c9ebc84..77a9871 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
@@ -78,7 +78,7 @@
         mNotificationAppearDistance = getResources().getDimensionPixelSize(
                 R.dimen.notification_appear_distance);
         mNotificationHeaderHeight = getResources().getDimensionPixelSize(
-                R.dimen.notification_header_height);
+                com.android.internal.R.dimen.notification_header_height);
         mHeaderTopPaddingSubstraction = 2 * getResources().getDisplayMetrics().density;
         mCollapsedBottompadding = 10 * getResources().getDisplayMetrics().density;
         mHybridViewManager = new HybridNotificationViewManager(getContext(), this);
@@ -246,7 +246,7 @@
      *         in @param maxAllowedVisibleChildren
      */
     private int getIntrinsicHeight(float maxAllowedVisibleChildren) {
-        int intrinsicHeight = 0;
+        int intrinsicHeight = mNotificationHeaderHeight;;
         int visibleChildren = 0;
         int childCount = mChildren.size();
         for (int i = 0; i < childCount; i++) {
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 aeca97c..ae6b729 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -272,7 +272,7 @@
     @Override
     protected void onDraw(Canvas canvas) {
         if (DEBUG) {
-            int y = mCollapsedSize;
+            int y = mTopPadding;
             canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
             y = (int) (getLayoutHeight() - mBottomStackPeekSize
                     - mBottomStackSlowDownHeight);
@@ -550,8 +550,9 @@
         return Math.min(mMaxLayoutHeight, mCurrentStackHeight);
     }
 
-    public int getItemHeight() {
-        return mCollapsedSize;
+    public int getFirstItemMinHeight() {
+        final ExpandableView firstChild = getFirstChildNotGone();
+        return firstChild != null ? firstChild.getMinHeight() : mCollapsedSize;
     }
 
     public int getBottomStackPeekSize() {
@@ -1321,14 +1322,14 @@
         ExpandableView firstChild = (ExpandableView) getFirstChildNotGone();
         if (firstChild != null) {
             int contentHeight = getContentHeight();
-            int firstChildMaxExpandHeight = getMaxExpandHeight(firstChild);
             scrollRange = Math.max(0, contentHeight - mMaxLayoutHeight + mBottomStackPeekSize
                     + mBottomStackSlowDownHeight);
             if (scrollRange > 0) {
-                View lastChild = getLastChildNotGone();
+                int firstChildMaxExpandHeight = getMaxExpandHeight(firstChild);
                 // We want to at least be able collapse the first item and not ending in a weird
                 // end state.
-                scrollRange = Math.max(scrollRange, firstChildMaxExpandHeight - mCollapsedSize);
+                scrollRange = Math.max(scrollRange, firstChildMaxExpandHeight
+                        - firstChild.getMinHeight());
             }
         }
         return scrollRange;
@@ -1337,12 +1338,12 @@
     /**
      * @return the first child which has visibility unequal to GONE
      */
-    private View getFirstChildNotGone() {
+    private ExpandableView getFirstChildNotGone() {
         int childCount = getChildCount();
         for (int i = 0; i < childCount; i++) {
             View child = getChildAt(i);
             if (child.getVisibility() != View.GONE) {
-                return child;
+                return (ExpandableView) child;
             }
         }
         return null;
@@ -1504,7 +1505,10 @@
     }
 
     public int getMinStackHeight() {
-        return mCollapsedSize + mBottomStackPeekSize + mCollapseSecondCardPadding;
+        final ExpandableView firstChild = getFirstChildNotGone();
+        final int firstChildMinHeight = firstChild != null ? (int) firstChild.getMinHeight()
+                : mCollapsedSize;
+        return firstChildMinHeight + mBottomStackPeekSize + mCollapseSecondCardPadding;
     }
 
     public float getTopPaddingOverflow() {
@@ -1512,7 +1516,10 @@
     }
 
     public int getPeekHeight() {
-        return mIntrinsicPadding + mCollapsedSize + mBottomStackPeekSize
+        final ExpandableView firstChild = getFirstChildNotGone();
+        final int firstChildMinHeight = firstChild != null ? (int) firstChild.getMinHeight()
+                : mCollapsedSize;
+        return mIntrinsicPadding + firstChildMinHeight + mBottomStackPeekSize
                 + mCollapseSecondCardPadding;
     }
 
@@ -1860,7 +1867,7 @@
             }
             mNeedsAnimation = true;
         }
-        if (isHeadsUp(child)) {
+        if (isHeadsUp(child) && !mChangePositionInProgress) {
             mAddedHeadsUpChildren.add(child);
             mChildrenToAddAnimated.remove(child);
         }
@@ -2266,6 +2273,11 @@
         return Math.max(emptyMargin, 0);
     }
 
+    public float getKeyguardBottomStackSize() {
+        return mBottomStackPeekSize + getResources().getDimensionPixelSize(
+                R.dimen.bottom_stack_slow_down_length);
+    }
+
     public void onExpansionStarted() {
         mIsExpansionChanging = true;
         mStackScrollAlgorithm.onExpansionStarted(mCurrentStackScrollState);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
index 65ca95b..953f287 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
@@ -42,7 +42,7 @@
     private static final int MAX_ITEMS_IN_BOTTOM_STACK = 3;
     private static final int MAX_ITEMS_IN_TOP_STACK = 3;
 
-    public static final float DIMMED_SCALE = 0.95f;
+    public static final float DIMMED_SCALE = 0.98f;
 
     private int mPaddingBetweenElements;
     private int mCollapsedSize;
@@ -72,6 +72,7 @@
     private int mMaxNotificationHeight;
     private boolean mScaleDimmed;
     private HeadsUpManager mHeadsUpManager;
+    private int mFirstChildMinHeight;
 
     public StackScrollAlgorithm(Context context) {
         initConstants(context);
@@ -155,7 +156,7 @@
         // Due to the overScroller, the stackscroller can have negative scroll state. This is
         // already accounted for by the top padding and doesn't need an additional adaption
         scrollY = Math.max(0, scrollY);
-        algorithmState.scrollY = (int) (scrollY + mCollapsedSize + bottomOverScroll);
+        algorithmState.scrollY = (int) (scrollY + mFirstChildMinHeight + bottomOverScroll);
 
         updateVisibleChildren(resultState, algorithmState);
 
@@ -424,7 +425,8 @@
             float yPositionInScrollViewAfterElement = yPositionInScrollView
                     + childHeight
                     + mPaddingBetweenElements;
-            float scrollOffset = yPositionInScrollView - algorithmState.scrollY + mCollapsedSize;
+            float scrollOffset = yPositionInScrollView - algorithmState.scrollY +
+                    mFirstChildMinHeight;
 
             if (i == algorithmState.lastTopStackIndex + 1) {
                 // Normally the position of this child is the position in the regular scrollview,
@@ -451,10 +453,10 @@
                         >= bottomStackStart && !mIsExpansionChanging && i != 0 && mIsSmallScreen) {
                     // we just collapse this element slightly
                     int newSize = (int) Math.max(bottomStackStart - mPaddingBetweenElements -
-                            childViewState.yTranslation, mCollapsedSize);
+                            childViewState.yTranslation, child.getMinHeight());
                     childViewState.height = newSize;
                     updateStateForChildTransitioningInBottom(algorithmState, bottomStackStart,
-                            bottomPeekStart, childViewState.yTranslation, childViewState,
+                            child, childViewState.yTranslation, childViewState,
                             childHeight);
                 }
                 clampPositionToBottomStackStart(childViewState, childViewState.height,
@@ -471,7 +473,7 @@
                     // According to the regular scroll view we are currently translating out of /
                     // into the bottom of the screen
                     updateStateForChildTransitioningInBottom(algorithmState,
-                            bottomStackStart, bottomPeekStart, currentYPosition,
+                            bottomStackStart, child, currentYPosition,
                             childViewState, childHeight);
                 }
             } else {
@@ -484,12 +486,13 @@
             // The first card is always rendered.
             if (i == 0) {
                 childViewState.alpha = 1.0f;
-                childViewState.yTranslation = Math.max(mCollapsedSize - algorithmState.scrollY, 0);
+                childViewState.yTranslation = Math.max(
+                        mFirstChildMinHeight - algorithmState.scrollY, 0);
                 if (childViewState.yTranslation + childViewState.height
                         > bottomPeekStart - mCollapseSecondCardPadding) {
                     childViewState.height = (int) Math.max(
                             bottomPeekStart - mCollapseSecondCardPadding
-                                    - childViewState.yTranslation, mCollapsedSize);
+                                    - childViewState.yTranslation, mFirstChildMinHeight);
                 }
                 childViewState.location = StackViewState.LOCATION_FIRST_CARD;
             }
@@ -501,7 +504,8 @@
 
             if (ambientState.isShadeExpanded() && topHeadsUpEntry != null
                     && child != topHeadsUpEntry) {
-                childViewState.yTranslation += topHeadsUpEntry.getHeadsUpHeight() - mCollapsedSize;
+                childViewState.yTranslation += topHeadsUpEntry.getHeadsUpHeight() -
+                        mFirstChildMinHeight;
             }
             childViewState.yTranslation += ambientState.getTopPadding()
                     + ambientState.getStackTranslation();
@@ -528,7 +532,7 @@
             boolean isTopEntry = topHeadsUpEntry == row;
             if (mIsExpanded) {
                 if (isTopEntry) {
-                    childState.height += row.getHeadsUpHeight() - mCollapsedSize;
+                    childState.height += row.getHeadsUpHeight() - mFirstChildMinHeight;
                 }
                 childState.height = Math.max(childState.height, row.getHeadsUpHeight());
                 // Ensure that the heads up is always visible even when scrolled off from the bottom
@@ -588,7 +592,7 @@
     private void clampPositionToTopStackEnd(StackViewState childViewState,
             int childHeight) {
         childViewState.yTranslation = Math.max(childViewState.yTranslation,
-                mCollapsedSize - childHeight);
+                mFirstChildMinHeight - childHeight);
     }
 
     private int getMaxAllowedChildHeight(View child, AmbientState ambientState) {
@@ -597,7 +601,7 @@
             if (ambientState == null && row.isHeadsUp()
                     || ambientState != null && ambientState.getTopHeadsUpEntry() == child) {
                 int extraSize = row.getIntrinsicHeight() - row.getHeadsUpHeight();
-                return mCollapsedSize + extraSize;
+                return mFirstChildMinHeight + extraSize;
             }
             return row.getIntrinsicHeight();
         } else if (child instanceof ExpandableView) {
@@ -608,7 +612,7 @@
     }
 
     private void updateStateForChildTransitioningInBottom(StackScrollAlgorithmState algorithmState,
-            float transitioningPositionStart, float bottomPeakStart, float currentYPosition,
+            float transitioningPositionStart, ExpandableView child, float currentYPosition,
             StackViewState childViewState, int childHeight) {
 
         // This is the transitioning element on top of bottom stack, calculate how far we are in.
@@ -620,9 +624,10 @@
         float offset = mBottomStackIndentationFunctor.getValue(algorithmState.partialInBottom);
         algorithmState.itemsInBottomStack += algorithmState.partialInBottom;
         int newHeight = childHeight;
-        if (childHeight > mCollapsedSize && mIsSmallScreen) {
+        if (childHeight > child.getMinHeight() && mIsSmallScreen) {
             newHeight = (int) Math.max(Math.min(transitioningPositionStart + offset -
-                    mPaddingBetweenElements - currentYPosition, childHeight), mCollapsedSize);
+                    mPaddingBetweenElements - currentYPosition, childHeight),
+                    child.getMinHeight());
             childViewState.height = newHeight;
         }
         childViewState.yTranslation = transitioningPositionStart + offset - newHeight
@@ -689,7 +694,7 @@
                     numItemsBefore = algorithmState.itemsInTopStack - i;
                 }
                 // The end position of the current child
-                float currentChildEndY = mCollapsedSize + mTopStackTotalSize
+                float currentChildEndY = mFirstChildMinHeight + mTopStackTotalSize
                         - mTopStackIndentationFunctor.getValue(numItemsBefore);
                 childViewState.yTranslation = currentChildEndY - childHeight;
             }
@@ -701,7 +706,7 @@
                 // We are hidden behind the top card and faded out, so we can hide ourselves.
                 childViewState.alpha = 0.0f;
             }
-            childViewState.yTranslation = mCollapsedSize - childHeight;
+            childViewState.yTranslation = mFirstChildMinHeight - childHeight;
             childViewState.location = StackViewState.LOCATION_TOP_STACK_HIDDEN;
         }
 
@@ -730,7 +735,7 @@
                     + childHeight
                     + mPaddingBetweenElements;
             if (yPositionInScrollView < algorithmState.scrollY) {
-                if (i == 0 && algorithmState.scrollY <= mCollapsedSize) {
+                if (i == 0 && algorithmState.scrollY <= mFirstChildMinHeight) {
 
                     // The starting position of the bottom stack peek
                     int bottomPeekStart = ambientState.getInnerHeight() - mBottomStackPeekSize -
@@ -740,14 +745,14 @@
                             ? mFirstChildMaxHeight
                             : childHeight;
                     childViewState.height = (int) Math.max(Math.min(bottomPeekStart, maxHeight),
-                            mCollapsedSize);
+                            mFirstChildMinHeight);
                     algorithmState.itemsInTopStack = 1.0f;
 
                 } else if (yPositionInScrollViewAfterElement < algorithmState.scrollY) {
                     // According to the regular scroll view we are fully off screen
                     algorithmState.itemsInTopStack += 1.0f;
                     if (i == 0) {
-                        childViewState.height = mCollapsedSize;
+                        childViewState.height = child.getMinHeight();
                     }
                 } else {
                     // According to the regular scroll view we are partially off screen
@@ -766,8 +771,8 @@
                         // If it is expanded we have to collapse it to a new size
                         float newSize = yPositionInScrollViewAfterElement
                                 - mPaddingBetweenElements
-                                - algorithmState.scrollY + mCollapsedSize;
-                        newSize = Math.max(mCollapsedSize, newSize);
+                                - algorithmState.scrollY + mFirstChildMinHeight;
+                        newSize = Math.max(mFirstChildMinHeight, newSize);
                         algorithmState.itemsInTopStack = 1.0f;
                         childViewState.height = (int) newSize;
                     }
@@ -809,7 +814,7 @@
                     // Interpolate the index from 0 to 2 while the second item is
                     // translating in.
                     stackIndex -= 1.0f;
-                    if (algorithmState.scrollY > mCollapsedSize) {
+                    if (algorithmState.scrollY > mFirstChildMinHeight) {
 
                         // Since there is a shadow treshhold, we cant just interpolate from 0 to
                         // 2 but we interpolate from 0.1f to 2.0f when scrolled in. The jump in
@@ -863,7 +868,7 @@
                     ExpandableNotificationRow row =
                             (ExpandableNotificationRow) mFirstChildWhileExpanding;
                     if (row.isHeadsUp()) {
-                        mFirstChildMaxHeight += mCollapsedSize - row.getHeadsUpHeight();
+                        mFirstChildMaxHeight += mFirstChildMinHeight - row.getHeadsUpHeight();
                     }
                 }
             } else {
@@ -927,7 +932,11 @@
         this.mIsExpanded = isExpanded;
     }
 
-    public void notifyChildrenChanged(final ViewGroup hostView) {
+    public void notifyChildrenChanged(final NotificationStackScrollLayout hostView) {
+        int firstItemMinHeight = hostView.getFirstItemMinHeight();
+        if (firstItemMinHeight != mFirstChildMinHeight) {
+            mFirstChildMinHeight = firstItemMinHeight;
+        }
         if (mIsExpansionChanging) {
             hostView.post(new Runnable() {
                 @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
index bbe5dd9..39a2986 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
@@ -130,7 +130,7 @@
     }
 
     @Override
-    protected int getMaxKeyguardNotifications() {
+    protected int getMaxKeyguardNotifications(boolean recompute) {
         return 0;
     }
 
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 79c0957..2301c41 100644
--- a/preloaded-classes
+++ b/preloaded-classes
@@ -1765,6 +1765,7 @@
 android.util.SuperNotCalledException
 android.util.TypedValue
 android.util.Xml
+android.util.jar.StrictJarFile
 android.view.AbsSavedState
 android.view.AbsSavedState$1
 android.view.AbsSavedState$2
@@ -3361,9 +3362,6 @@
 java.util.jar.JarEntry
 java.util.jar.JarFile
 java.util.jar.JarFile$JarFileEnumerator
-java.util.jar.Manifest
-java.util.jar.ManifestReader
-java.util.jar.StrictJarFile
 java.util.logging.ConsoleHandler
 java.util.logging.ErrorManager
 java.util.logging.Filter
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 80d00cc..3ef0723 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();
@@ -7149,22 +7212,35 @@
 
         @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 (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) {
@@ -7172,6 +7248,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;
+                }
             }
         }
     }
@@ -9945,14 +10024,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);
@@ -10045,6 +10116,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 =
@@ -10197,6 +10278,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) {
@@ -10900,7 +11027,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);
         }
 
@@ -17762,7 +17889,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;
 
@@ -20300,8 +20427,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 c3bd982..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;
     }
 
@@ -3114,7 +3160,7 @@
                     for (int i = 0; i < count; i++) {
                         moveTaskToStackLocked(tasks.get(i).taskId,
                                 FULLSCREEN_WORKSPACE_STACK_ID, ON_TOP, FORCE_FOCUS, "resizeStack",
-                                true /* animate */);
+                                false /* animate */);
                     }
 
                     // stack shouldn't contain anymore activities, so nothing to resume.
@@ -3376,6 +3422,10 @@
         final ActivityStack stack = moveTaskToStackUncheckedLocked(
                 task, stackId, toTop, forceFocus, "moveTaskToStack:" + reason);
 
+        if (!animate) {
+            stack.mNoAnimActivities.add(topActivity);
+        }
+
         // Make sure the task has the appropriate bounds/size for the stack it is in.
         if (stackId == FULLSCREEN_WORKSPACE_STACK_ID && task.mBounds != null) {
             resizeTaskLocked(task, stack.mBounds, RESIZE_MODE_SYSTEM, !PRESERVE_WINDOWS);
@@ -3393,7 +3443,7 @@
         resumeTopActivitiesLocked();
 
         if (!task.mResizeable && isStackDockedInEffect(stackId)) {
-            showNonResizeableDockToast();
+            showNonResizeableDockToast(taskId);
         }
     }
 
@@ -4327,8 +4377,8 @@
         }
     }
 
-    void showNonResizeableDockToast() {
-        mHandler.sendEmptyMessage(SHOW_NON_RESIZEABLE_DOCK_TOAST);
+    private void showNonResizeableDockToast(int taskId) {
+        mWindowManager.scheduleShowNonResizeableDockToast(taskId);
     }
 
     void showLockTaskToast() {
@@ -4643,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/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..12bd7f9 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) {
@@ -1074,6 +1158,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/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index f92f631..246da2e 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -63,7 +63,7 @@
  */
 public class MediaSessionRecord implements IBinder.DeathRecipient {
     private static final String TAG = "MediaSessionRecord";
-    private static final boolean DEBUG = false;
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
     /**
      * The length of time a session will still be considered active after
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index 90dd10ea..d5c3113 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -287,6 +287,9 @@
      * 6. We need to tell the session to do any final cleanup (onDestroy)
      */
     private void destroySessionLocked(MediaSessionRecord session) {
+        if (DEBUG) {
+            Log.d(TAG, "Destroying session : " + session.toString());
+        }
         int userId = session.getUserId();
         UserRecord user = mUserRecords.get(userId);
         if (user != null) {
diff --git a/services/core/java/com/android/server/media/MediaSessionStack.java b/services/core/java/com/android/server/media/MediaSessionStack.java
index 611718e..61c320b 100644
--- a/services/core/java/com/android/server/media/MediaSessionStack.java
+++ b/services/core/java/com/android/server/media/MediaSessionStack.java
@@ -16,13 +16,17 @@
 
 package com.android.server.media;
 
+import android.app.ActivityManager;
+import android.app.ActivityManagerNative;
 import android.media.session.MediaController.PlaybackInfo;
 import android.media.session.PlaybackState;
 import android.media.session.MediaSession;
+import android.os.RemoteException;
 import android.os.UserHandle;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.List;
 
 /**
  * Keeps track of media sessions and their priority for notifications, media
@@ -61,6 +65,36 @@
     private ArrayList<MediaSessionRecord> mCachedTransportControlList;
 
     /**
+     * Checks if a media session is created from the most recent app.
+     *
+     * @param record A media session record to be examined.
+     * @return true if the media session's package name equals to the most recent app, false
+     * otherwise.
+     */
+    private static boolean isFromMostRecentApp(MediaSessionRecord record) {
+        if (ActivityManager.getCurrentUser() != record.getUserId()) {
+            return false;
+        }
+        try {
+            List<ActivityManager.RecentTaskInfo> tasks =
+                    ActivityManagerNative.getDefault().getRecentTasks(1,
+                            ActivityManager.RECENT_IGNORE_HOME_STACK_TASKS |
+                            ActivityManager.RECENT_IGNORE_UNAVAILABLE |
+                            ActivityManager.RECENT_INCLUDE_PROFILES |
+                            ActivityManager.RECENT_WITH_EXCLUDED, record.getUserId());
+            if (tasks != null && !tasks.isEmpty()) {
+                ActivityManager.RecentTaskInfo recentTask = tasks.get(0);
+                if (recentTask.baseIntent != null)
+                    return recentTask.baseIntent.getComponent().getPackageName()
+                            .equals(record.getPackageName());
+            }
+        } catch (RemoteException e) {
+            return false;
+        }
+        return false;
+    }
+
+    /**
      * Add a record to the priority tracker.
      *
      * @param record The record to add.
@@ -68,7 +102,9 @@
     public void addSession(MediaSessionRecord record) {
         mSessions.add(record);
         clearCache();
-        mLastInterestingRecord = record;
+        if (isFromMostRecentApp(record)) {
+            mLastInterestingRecord = record;
+        }
     }
 
     /**
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 395962c..1bb5ad0 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);
@@ -7452,6 +7467,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.
@@ -8024,6 +8040,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");
@@ -9104,8 +9131,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/test-runner/src/android/test/mock/MockContext.java b/test-runner/src/android/test/mock/MockContext.java
index 4821678..c967c2b 100644
--- a/test-runner/src/android/test/mock/MockContext.java
+++ b/test-runner/src/android/test/mock/MockContext.java
@@ -147,6 +147,11 @@
     }
 
     @Override
+    public SharedPreferences getSharedPreferences(File file, int mode) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
     public FileInputStream openFileInput(String name) throws FileNotFoundException {
         throw new UnsupportedOperationException();
     }
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/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/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
index 0fcfa78..ff15f3b 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
@@ -1438,6 +1438,14 @@
     }
 
     @Override
+    public SharedPreferences getSharedPreferences(File arg0, int arg1) {
+        if (mSharedPreferences == null) {
+            mSharedPreferences = new BridgeSharedPreferences();
+        }
+        return mSharedPreferences;
+    }
+
+    @Override
     public Drawable getWallpaper() {
         // pass
         return null;
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 {