Merge "Connect the DHCP UDP socket to the server."
diff --git a/api/current.txt b/api/current.txt
index e3c9366..ef18bf9 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -4021,6 +4021,8 @@
     ctor public AutomaticZenRule(android.os.Parcel);
     method public int describeContents();
     method public android.net.Uri getConditionId();
+    method public long getCreationTime();
+    method public java.lang.String getId();
     method public int getInterruptionFilter();
     method public java.lang.String getName();
     method public android.content.ComponentName getOwner();
@@ -5078,7 +5080,7 @@
   }
 
   public class NotificationManager {
-    method public boolean addOrUpdateAutomaticZenRule(android.app.AutomaticZenRule);
+    method public android.app.AutomaticZenRule addAutomaticZenRule(android.app.AutomaticZenRule);
     method public void cancel(int);
     method public void cancel(java.lang.String, int);
     method public void cancelAll();
@@ -5091,9 +5093,9 @@
     method public void notify(int, android.app.Notification);
     method public void notify(java.lang.String, int, android.app.Notification);
     method public boolean removeAutomaticZenRule(java.lang.String);
-    method public boolean renameAutomaticZenRule(java.lang.String, java.lang.String);
     method public final void setInterruptionFilter(int);
     method public void setNotificationPolicy(android.app.NotificationManager.Policy);
+    method public boolean updateAutomaticZenRule(android.app.AutomaticZenRule);
     field public static final java.lang.String ACTION_INTERRUPTION_FILTER_CHANGED = "android.app.action.INTERRUPTION_FILTER_CHANGED";
     field public static final java.lang.String ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED = "android.app.action.NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED";
     field public static final java.lang.String ACTION_NOTIFICATION_POLICY_CHANGED = "android.app.action.NOTIFICATION_POLICY_CHANGED";
@@ -5629,7 +5631,7 @@
     method public void onPasswordFailed(android.content.Context, android.content.Intent);
     method public void onPasswordSucceeded(android.content.Context, android.content.Intent);
     method public void onProfileProvisioningComplete(android.content.Context, android.content.Intent);
-    method public void onReadyForUserInitialization(android.content.Context, android.content.Intent);
+    method public deprecated void onReadyForUserInitialization(android.content.Context, android.content.Intent);
     method public void onReceive(android.content.Context, android.content.Intent);
     method public void onSystemUpdatePending(android.content.Context, android.content.Intent, long);
     field public static final java.lang.String ACTION_DEVICE_ADMIN_DISABLED = "android.app.action.DEVICE_ADMIN_DISABLED";
@@ -9235,6 +9237,7 @@
     method public void setAppLabel(java.lang.CharSequence);
     method public void setAppPackageName(java.lang.String);
     method public void setInstallLocation(int);
+    method public void setOriginatingUid(int);
     method public void setOriginatingUri(android.net.Uri);
     method public void setReferrerUri(android.net.Uri);
     method public void setSize(long);
@@ -22847,6 +22850,7 @@
     field public static final int LOLLIPOP = 21; // 0x15
     field public static final int LOLLIPOP_MR1 = 22; // 0x16
     field public static final int M = 23; // 0x17
+    field public static final int N = 10000; // 0x2710
   }
 
   public final class Bundle extends android.os.BaseBundle implements java.lang.Cloneable android.os.Parcelable {
@@ -24961,6 +24965,7 @@
   public static class CallLog.Calls implements android.provider.BaseColumns {
     ctor public CallLog.Calls();
     method public static java.lang.String getLastOutgoingCall(android.content.Context);
+    field public static final int BLOCKED_TYPE = 6; // 0x6
     field public static final java.lang.String CACHED_FORMATTED_NUMBER = "formatted_number";
     field public static final java.lang.String CACHED_LOOKUP_URI = "lookup_uri";
     field public static final java.lang.String CACHED_MATCHED_NUMBER = "matched_number";
@@ -24999,6 +25004,7 @@
     field public static final int PRESENTATION_PAYPHONE = 4; // 0x4
     field public static final int PRESENTATION_RESTRICTED = 2; // 0x2
     field public static final int PRESENTATION_UNKNOWN = 3; // 0x3
+    field public static final int REJECTED_TYPE = 5; // 0x5
     field public static final java.lang.String TRANSCRIPTION = "transcription";
     field public static final java.lang.String TYPE = "type";
     field public static final int VOICEMAIL_TYPE = 4; // 0x4
@@ -28845,10 +28851,8 @@
     method public abstract void onRequestConditions(int);
     method public abstract void onSubscribe(android.net.Uri);
     method public abstract void onUnsubscribe(android.net.Uri);
-    field public static final java.lang.String EXTRA_CONDITION_ID = "android.content.automatic.conditionId";
-    field public static final java.lang.String EXTRA_RULE_NAME = "android.content.automatic.ruleName";
+    field public static final java.lang.String EXTRA_RULE_ID = "android.content.automatic.ruleId";
     field public static final java.lang.String META_DATA_CONFIGURATION_ACTIVITY = "android.service.zen.automatic.configurationActivity";
-    field public static final java.lang.String META_DATA_DEFAULT_CONDITION_ID = "android.service.zen.automatic.defaultConditionId";
     field public static final java.lang.String META_DATA_RULE_TYPE = "android.service.zen.automatic.ruleType";
     field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.ConditionProviderService";
   }
@@ -30474,6 +30478,7 @@
     method public android.telecom.PhoneAccountHandle getAccountHandle();
     method public android.net.Uri getAddress();
     method public int getCapabilities();
+    method public android.os.Bundle getExtras();
     method public int getHighlightColor();
     method public android.graphics.drawable.Icon getIcon();
     method public java.lang.CharSequence getLabel();
@@ -30506,6 +30511,7 @@
     method public android.telecom.PhoneAccount build();
     method public android.telecom.PhoneAccount.Builder setAddress(android.net.Uri);
     method public android.telecom.PhoneAccount.Builder setCapabilities(int);
+    method public android.telecom.PhoneAccount.Builder setExtras(android.os.Bundle);
     method public android.telecom.PhoneAccount.Builder setHighlightColor(int);
     method public android.telecom.PhoneAccount.Builder setIcon(android.graphics.drawable.Icon);
     method public android.telecom.PhoneAccount.Builder setShortDescription(java.lang.CharSequence);
diff --git a/api/removed.txt b/api/removed.txt
index 642d2a8..f12e61e 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -6,6 +6,15 @@
 
 }
 
+package android.app.admin {
+
+  public class DevicePolicyManager {
+    method public deprecated java.lang.String getDeviceInitializerApp();
+    method public deprecated android.content.ComponentName getDeviceInitializerComponent();
+  }
+
+}
+
 package android.content.pm {
 
   public class PackageInfo implements android.os.Parcelable {
diff --git a/api/system-current.txt b/api/system-current.txt
index b1590d8..0b216bdd 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -4132,6 +4132,8 @@
     ctor public AutomaticZenRule(android.os.Parcel);
     method public int describeContents();
     method public android.net.Uri getConditionId();
+    method public long getCreationTime();
+    method public java.lang.String getId();
     method public int getInterruptionFilter();
     method public java.lang.String getName();
     method public android.content.ComponentName getOwner();
@@ -5195,7 +5197,7 @@
   }
 
   public class NotificationManager {
-    method public boolean addOrUpdateAutomaticZenRule(android.app.AutomaticZenRule);
+    method public android.app.AutomaticZenRule addAutomaticZenRule(android.app.AutomaticZenRule);
     method public void cancel(int);
     method public void cancel(java.lang.String, int);
     method public void cancelAll();
@@ -5208,9 +5210,9 @@
     method public void notify(int, android.app.Notification);
     method public void notify(java.lang.String, int, android.app.Notification);
     method public boolean removeAutomaticZenRule(java.lang.String);
-    method public boolean renameAutomaticZenRule(java.lang.String, java.lang.String);
     method public final void setInterruptionFilter(int);
     method public void setNotificationPolicy(android.app.NotificationManager.Policy);
+    method public boolean updateAutomaticZenRule(android.app.AutomaticZenRule);
     field public static final java.lang.String ACTION_INTERRUPTION_FILTER_CHANGED = "android.app.action.INTERRUPTION_FILTER_CHANGED";
     field public static final java.lang.String ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED = "android.app.action.NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED";
     field public static final java.lang.String ACTION_NOTIFICATION_POLICY_CHANGED = "android.app.action.NOTIFICATION_POLICY_CHANGED";
@@ -5750,7 +5752,7 @@
     method public void onPasswordFailed(android.content.Context, android.content.Intent);
     method public void onPasswordSucceeded(android.content.Context, android.content.Intent);
     method public void onProfileProvisioningComplete(android.content.Context, android.content.Intent);
-    method public void onReadyForUserInitialization(android.content.Context, android.content.Intent);
+    method public deprecated void onReadyForUserInitialization(android.content.Context, android.content.Intent);
     method public void onReceive(android.content.Context, android.content.Intent);
     method public void onSystemUpdatePending(android.content.Context, android.content.Intent, long);
     field public static final java.lang.String ACTION_DEVICE_ADMIN_DISABLED = "android.app.action.DEVICE_ADMIN_DISABLED";
@@ -5792,8 +5794,8 @@
     method public boolean getCrossProfileCallerIdDisabled(android.content.ComponentName);
     method public java.util.List<java.lang.String> getCrossProfileWidgetProviders(android.content.ComponentName);
     method public int getCurrentFailedPasswordAttempts();
-    method public java.lang.String getDeviceInitializerApp();
-    method public android.content.ComponentName getDeviceInitializerComponent();
+    method public deprecated java.lang.String getDeviceInitializerApp();
+    method public deprecated android.content.ComponentName getDeviceInitializerComponent();
     method public java.lang.String getDeviceOwner();
     method public java.util.List<byte[]> getInstalledCaCerts(android.content.ComponentName);
     method public int getKeyguardDisabledFeatures(android.content.ComponentName);
@@ -9518,6 +9520,7 @@
     method public void setAppPackageName(java.lang.String);
     method public void setGrantedRuntimePermissions(java.lang.String[]);
     method public void setInstallLocation(int);
+    method public void setOriginatingUid(int);
     method public void setOriginatingUri(android.net.Uri);
     method public void setReferrerUri(android.net.Uri);
     method public void setSize(long);
@@ -24791,6 +24794,7 @@
     field public static final int LOLLIPOP = 21; // 0x15
     field public static final int LOLLIPOP_MR1 = 22; // 0x16
     field public static final int M = 23; // 0x17
+    field public static final int N = 10000; // 0x2710
   }
 
   public final class Bundle extends android.os.BaseBundle implements java.lang.Cloneable android.os.Parcelable {
@@ -26917,6 +26921,7 @@
   public static class CallLog.Calls implements android.provider.BaseColumns {
     ctor public CallLog.Calls();
     method public static java.lang.String getLastOutgoingCall(android.content.Context);
+    field public static final int BLOCKED_TYPE = 6; // 0x6
     field public static final java.lang.String CACHED_FORMATTED_NUMBER = "formatted_number";
     field public static final java.lang.String CACHED_LOOKUP_URI = "lookup_uri";
     field public static final java.lang.String CACHED_MATCHED_NUMBER = "matched_number";
@@ -26955,6 +26960,7 @@
     field public static final int PRESENTATION_PAYPHONE = 4; // 0x4
     field public static final int PRESENTATION_RESTRICTED = 2; // 0x2
     field public static final int PRESENTATION_UNKNOWN = 3; // 0x3
+    field public static final int REJECTED_TYPE = 5; // 0x5
     field public static final java.lang.String TRANSCRIPTION = "transcription";
     field public static final java.lang.String TYPE = "type";
     field public static final int VOICEMAIL_TYPE = 4; // 0x4
@@ -30934,10 +30940,8 @@
     method public abstract void onRequestConditions(int);
     method public abstract void onSubscribe(android.net.Uri);
     method public abstract void onUnsubscribe(android.net.Uri);
-    field public static final java.lang.String EXTRA_CONDITION_ID = "android.content.automatic.conditionId";
-    field public static final java.lang.String EXTRA_RULE_NAME = "android.content.automatic.ruleName";
+    field public static final java.lang.String EXTRA_RULE_ID = "android.content.automatic.ruleId";
     field public static final java.lang.String META_DATA_CONFIGURATION_ACTIVITY = "android.service.zen.automatic.configurationActivity";
-    field public static final java.lang.String META_DATA_DEFAULT_CONDITION_ID = "android.service.zen.automatic.defaultConditionId";
     field public static final java.lang.String META_DATA_RULE_TYPE = "android.service.zen.automatic.ruleType";
     field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.ConditionProviderService";
   }
@@ -32673,6 +32677,7 @@
     method public android.telecom.PhoneAccountHandle getAccountHandle();
     method public android.net.Uri getAddress();
     method public int getCapabilities();
+    method public android.os.Bundle getExtras();
     method public int getHighlightColor();
     method public android.graphics.drawable.Icon getIcon();
     method public java.lang.CharSequence getLabel();
@@ -32706,6 +32711,7 @@
     method public android.telecom.PhoneAccount build();
     method public android.telecom.PhoneAccount.Builder setAddress(android.net.Uri);
     method public android.telecom.PhoneAccount.Builder setCapabilities(int);
+    method public android.telecom.PhoneAccount.Builder setExtras(android.os.Bundle);
     method public android.telecom.PhoneAccount.Builder setHighlightColor(int);
     method public android.telecom.PhoneAccount.Builder setIcon(android.graphics.drawable.Icon);
     method public android.telecom.PhoneAccount.Builder setShortDescription(java.lang.CharSequence);
@@ -41876,6 +41882,7 @@
     method public void super_scrollTo(int, int);
     method public boolean super_setFrame(int, int, int, int);
     method public void super_setLayoutParams(android.view.ViewGroup.LayoutParams);
+    method public void super_startActivityForResult(android.content.Intent, int);
   }
 
   public static abstract class WebView.VisualStateCallback {
@@ -42110,6 +42117,7 @@
   public static abstract interface WebViewProvider.ViewDelegate {
     method public abstract boolean dispatchKeyEvent(android.view.KeyEvent);
     method public abstract android.view.accessibility.AccessibilityNodeProvider getAccessibilityNodeProvider();
+    method public abstract void onActivityResult(int, int, android.content.Intent);
     method public abstract void onAttachedToWindow();
     method public abstract void onConfigurationChanged(android.content.res.Configuration);
     method public abstract android.view.inputmethod.InputConnection onCreateInputConnection(android.view.inputmethod.EditorInfo);
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index 5c9fd51..4a13136 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -44,6 +44,7 @@
 import android.content.pm.IPackageManager;
 import android.content.pm.ParceledListSlice;
 import android.content.pm.ResolveInfo;
+import android.content.pm.UserInfo;
 import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.net.Uri;
@@ -63,6 +64,7 @@
 import android.view.IWindowManager;
 
 import com.android.internal.os.BaseCommand;
+import com.android.internal.util.Preconditions;
 
 import java.io.BufferedReader;
 import java.io.File;
@@ -168,6 +170,7 @@
                 "       am get-inactive [--user <USER_ID>] <PACKAGE>\n" +
                 "       am send-trim-memory [--user <USER_ID>] <PROCESS>\n" +
                 "               [HIDDEN|RUNNING_MODERATE|BACKGROUND|RUNNING_LOW|MODERATE|RUNNING_CRITICAL|COMPLETE]\n" +
+                "       am get-current-user\n" +
                 "\n" +
                 "am start: start an Activity.  Options are:\n" +
                 "    -D: enable debugging\n" +
@@ -334,7 +337,9 @@
                 "\n" +
                 "am get-inactive: returns the inactive state of an app.\n" +
                 "\n" +
-                "am send-trim-memory: Send a memory trim event to a <PROCESS>.\n" +
+                "am send-trim-memory: send a memory trim event to a <PROCESS>.\n" +
+                "\n" +
+                "am get-current-user: returns id of the current foreground user.\n" +
                 "\n" +
                 "<INTENT> specifications include these flags and arguments:\n" +
                 "    [-a <ACTION>] [-d <DATA_URI>] [-t <MIME_TYPE>]\n" +
@@ -464,6 +469,8 @@
             runGetInactive();
         } else if (op.equals("send-trim-memory")) {
             runSendTrimMemory();
+        } else if (op.equals("get-current-user")) {
+            runGetCurrentUser();
         } else {
             showError("Error: unknown command '" + op + "'");
         }
@@ -2712,6 +2719,12 @@
         }
     }
 
+    private void runGetCurrentUser() throws Exception {
+        UserInfo currentUser = Preconditions.checkNotNull(mAm.getCurrentUser(),
+                "Current user not set");
+        System.out.println(currentUser.id);
+    }
+
     /**
      * Open the given file for sending into the system process. This verifies
      * with SELinux that the system will have access to the file.
diff --git a/cmds/dpm/src/com/android/commands/dpm/Dpm.java b/cmds/dpm/src/com/android/commands/dpm/Dpm.java
index 214dc5d..ea53009 100644
--- a/cmds/dpm/src/com/android/commands/dpm/Dpm.java
+++ b/cmds/dpm/src/com/android/commands/dpm/Dpm.java
@@ -44,6 +44,7 @@
 
     private IDevicePolicyManager mDevicePolicyManager;
     private int mUserId = UserHandle.USER_SYSTEM;
+    private String mName = "";
     private ComponentName mComponent = null;
 
     @Override
@@ -52,8 +53,8 @@
                 "usage: dpm [subcommand] [options]\n" +
                 "usage: dpm set-active-admin [ --user <USER_ID> ] <COMPONENT>\n" +
                 // STOPSHIP Finalize it
-                "usage: dpm set-device-owner [ --user <USER_ID> *EXPERIMENTAL* ] <COMPONENT>\n" +
-                "usage: dpm set-profile-owner [ --user <USER_ID> ] <COMPONENT>\n" +
+                "usage: dpm set-device-owner [ --user <USER_ID> *EXPERIMENTAL* ] [ --name <NAME> ] <COMPONENT>\n" +
+                "usage: dpm set-profile-owner [ --user <USER_ID> ] [ --name <NAME> ] <COMPONENT>\n" +
                 "\n" +
                 "dpm set-active-admin: Sets the given component as active admin" +
                 " for an existing user.\n" +
@@ -90,47 +91,51 @@
         }
     }
 
-    private void parseArgs(boolean canHaveUser) {
-        String nextArg = nextArgRequired();
-        if (canHaveUser && "--user".equals(nextArg)) {
-            mUserId = parseInt(nextArgRequired());
-            nextArg = nextArgRequired();
+    private void parseArgs(boolean canHaveUser, boolean canHaveName) {
+        String opt;
+        while ((opt = nextOption()) != null) {
+            if (canHaveUser && "--user".equals(opt)) {
+                mUserId = parseInt(nextArgRequired());
+            } else if (canHaveName && "--name".equals(opt)) {
+                mName = nextArgRequired();
+            } else {
+                throw new IllegalArgumentException("Unknown option: " + opt);
+            }
         }
-        mComponent = parseComponentName(nextArg);
+        mComponent = parseComponentName(nextArgRequired());
     }
 
     private void runSetActiveAdmin() throws RemoteException {
-        parseArgs(true);
+        parseArgs(/*canHaveUser=*/ true, /*canHaveName=*/ false);
         mDevicePolicyManager.setActiveAdmin(mComponent, true /*refreshing*/, mUserId);
 
         System.out.println("Success: Active admin set to component " + mComponent.toShortString());
     }
 
     private void runSetDeviceOwner() throws RemoteException {
-        parseArgs(true);
+        parseArgs(/*canHaveUser=*/ true, /*canHaveName=*/ true);
         mDevicePolicyManager.setActiveAdmin(mComponent, true /*refreshing*/, mUserId);
 
-        String packageName = mComponent.getPackageName();
         try {
-            if (!mDevicePolicyManager.setDeviceOwner(packageName, null /*ownerName*/, mUserId)) {
+            if (!mDevicePolicyManager.setDeviceOwner(mComponent, mName, mUserId)) {
                 throw new RuntimeException(
-                        "Can't set package " + packageName + " as device owner.");
+                        "Can't set package " + mComponent + " as device owner.");
             }
         } catch (Exception e) {
             // Need to remove the admin that we just added.
             mDevicePolicyManager.removeActiveAdmin(mComponent, UserHandle.USER_SYSTEM);
             throw e;
         }
-        System.out.println("Success: Device owner set to package " + packageName);
+        System.out.println("Success: Device owner set to package " + mComponent);
         System.out.println("Active admin set to component " + mComponent.toShortString());
     }
 
     private void runSetProfileOwner() throws RemoteException {
-        parseArgs(true);
+        parseArgs(/*canHaveUser=*/ true, /*canHaveName=*/ true);
         mDevicePolicyManager.setActiveAdmin(mComponent, true /*refreshing*/, mUserId);
 
         try {
-            if (!mDevicePolicyManager.setProfileOwner(mComponent, "" /*ownerName*/, mUserId)) {
+            if (!mDevicePolicyManager.setProfileOwner(mComponent, mName, mUserId)) {
                 throw new RuntimeException("Can't set component " + mComponent.toShortString() +
                         " as profile owner for user " + mUserId);
             }
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index ebf5085..393956f 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -624,7 +624,7 @@
                 }
             }
 
-            String grp = nextOption();
+            String grp = nextArg();
             ArrayList<String> groupList = new ArrayList<String>();
             if (groups) {
                 List<PermissionGroupInfo> infos =
diff --git a/core/java/android/animation/AnimatorSet.java b/core/java/android/animation/AnimatorSet.java
index 16f825d..d444638 100644
--- a/core/java/android/animation/AnimatorSet.java
+++ b/core/java/android/animation/AnimatorSet.java
@@ -603,7 +603,17 @@
         createDependencyGraph();
 
         // Now that all dependencies are set up, start the animations that should be started.
-        start(mRootNode);
+        boolean setIsEmpty = false;
+        if (mStartDelay > 0) {
+            start(mRootNode);
+        } else if (mNodes.size() > 1) {
+            // No delay, but there are other animators in the set
+            onChildAnimatorEnded(mDelayAnim);
+        } else {
+            // Set is empty, no delay, no other animation. Skip to end in this case
+            setIsEmpty = true;
+        }
+
         if (mListeners != null) {
             ArrayList<AnimatorListener> tmpListeners =
                     (ArrayList<AnimatorListener>) mListeners.clone();
@@ -612,18 +622,9 @@
                 tmpListeners.get(i).onAnimationStart(this);
             }
         }
-        if (mNodes.size() == 0 && mStartDelay == 0) {
-            // Handle unusual case where empty AnimatorSet is started - should send out
-            // end event immediately since the event will not be sent out at all otherwise
-            mStarted = false;
-            if (mListeners != null) {
-                ArrayList<AnimatorListener> tmpListeners =
-                        (ArrayList<AnimatorListener>) mListeners.clone();
-                int numListeners = tmpListeners.size();
-                for (int i = 0; i < numListeners; ++i) {
-                    tmpListeners.get(i).onAnimationEnd(this);
-                }
-            }
+        if (setIsEmpty) {
+            // In the case of empty AnimatorSet, we will trigger the onAnimationEnd() right away.
+            onChildAnimatorEnded(mDelayAnim);
         }
     }
 
@@ -751,44 +752,7 @@
         public void onAnimationEnd(Animator animation) {
             animation.removeListener(this);
             mAnimatorSet.mPlayingSet.remove(animation);
-            Node animNode = mAnimatorSet.mNodeMap.get(animation);
-            animNode.mEnded = true;
-
-            if (!mAnimatorSet.mTerminated) {
-                List<Node> children = animNode.mChildNodes;
-                // Start children animations, if any.
-                int childrenSize = children == null ? 0 : children.size();
-                for (int i = 0; i < childrenSize; i++) {
-                    if (children.get(i).mLatestParent == animNode) {
-                        mAnimatorSet.start(children.get(i));
-                    }
-                }
-                // Listeners are already notified of the AnimatorSet ending in cancel() or
-                // end(); the logic below only kicks in when animations end normally
-                boolean allDone = true;
-                // Traverse the tree and find if there's any unfinished node
-                int size = mAnimatorSet.mNodes.size();
-                for (int i = 0; i < size; i++) {
-                    if (!mAnimatorSet.mNodes.get(i).mEnded) {
-                        allDone = false;
-                        break;
-                    }
-                }
-                if (allDone) {
-                    // If this was the last child animation to end, then notify listeners that this
-                    // AnimatorSet has ended
-                    if (mAnimatorSet.mListeners != null) {
-                        ArrayList<AnimatorListener> tmpListeners =
-                                (ArrayList<AnimatorListener>) mAnimatorSet.mListeners.clone();
-                        int numListeners = tmpListeners.size();
-                        for (int i = 0; i < numListeners; ++i) {
-                            tmpListeners.get(i).onAnimationEnd(mAnimatorSet);
-                        }
-                    }
-                    mAnimatorSet.mStarted = false;
-                    mAnimatorSet.mPaused = false;
-                }
-            }
+            mAnimatorSet.onChildAnimatorEnded(animation);
         }
 
         // Nothing to do
@@ -801,6 +765,47 @@
 
     }
 
+    private void onChildAnimatorEnded(Animator animation) {
+        Node animNode = mNodeMap.get(animation);
+        animNode.mEnded = true;
+
+        if (!mTerminated) {
+            List<Node> children = animNode.mChildNodes;
+            // Start children animations, if any.
+            int childrenSize = children == null ? 0 : children.size();
+            for (int i = 0; i < childrenSize; i++) {
+                if (children.get(i).mLatestParent == animNode) {
+                    start(children.get(i));
+                }
+            }
+            // Listeners are already notified of the AnimatorSet ending in cancel() or
+            // end(); the logic below only kicks in when animations end normally
+            boolean allDone = true;
+            // Traverse the tree and find if there's any unfinished node
+            int size = mNodes.size();
+            for (int i = 0; i < size; i++) {
+                if (!mNodes.get(i).mEnded) {
+                    allDone = false;
+                    break;
+                }
+            }
+            if (allDone) {
+                // If this was the last child animation to end, then notify listeners that this
+                // AnimatorSet has ended
+                if (mListeners != null) {
+                    ArrayList<AnimatorListener> tmpListeners =
+                            (ArrayList<AnimatorListener>) mListeners.clone();
+                    int numListeners = tmpListeners.size();
+                    for (int i = 0; i < numListeners; ++i) {
+                        tmpListeners.get(i).onAnimationEnd(this);
+                    }
+                }
+                mStarted = false;
+                mPaused = false;
+            }
+        }
+    }
+
     /**
      * @hide
      */
diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java
index da9a8c8..9b905bd 100644
--- a/core/java/android/animation/ValueAnimator.java
+++ b/core/java/android/animation/ValueAnimator.java
@@ -625,14 +625,19 @@
     /**
      * Gets the current position of the animation in time, which is equal to the current
      * time minus the time that the animation started. An animation that is not yet started will
-     * return a value of zero.
+     * return a value of zero, unless the animation has has its play time set via
+     * {@link #setCurrentPlayTime(long)} or {@link #setCurrentFraction(float)}, in which case
+     * it will return the time that was set.
      *
      * @return The current position in time of the animation.
      */
     public long getCurrentPlayTime() {
-        if (!mInitialized || !mStarted) {
+        if (!mInitialized || (!mStarted && mSeekFraction < 0)) {
             return 0;
         }
+        if (mSeekFraction >= 0) {
+            return (long) (mUnscaledDuration * mSeekFraction);
+        }
         return AnimationUtils.currentAnimationTimeMillis() - mStartTime;
     }
 
@@ -669,6 +674,10 @@
      * The frame delay may be ignored when the animation system uses an external timing
      * source, such as the display refresh rate (vsync), to govern animations.
      *
+     * Note that this method should be called from the same thread that {@link #start()} is
+     * called in order to check the frame delay for that animation. A runtime exception will be
+     * thrown if the calling thread does not have a Looper.
+     *
      * @return the requested time between frames, in milliseconds
      */
     public static long getFrameDelay() {
@@ -685,6 +694,10 @@
      * The frame delay may be ignored when the animation system uses an external timing
      * source, such as the display refresh rate (vsync), to govern animations.
      *
+     * Note that this method should be called from the same thread that {@link #start()} is
+     * called in order to have the new frame delay take effect on that animation. A runtime
+     * exception will be thrown if the calling thread does not have a Looper.
+     *
      * @param frameDelay the requested time between frames, in milliseconds
      */
     public static void setFrameDelay(long frameDelay) {
@@ -924,6 +937,13 @@
         updateScaledDuration(); // in case the scale factor has changed since creation time
         AnimationHandler animationHandler = AnimationHandler.getInstance();
         animationHandler.addAnimationFrameCallback(this, mStartDelay);
+
+        if (mStartDelay == 0) {
+            // If there's no start delay, init the animation and notify start listeners right away
+            // Otherwise, postpone this until the first frame after the start delay.
+            startAnimation();
+            setCurrentFraction(mSeekFraction == -1 ? 0 : mSeekFraction);
+        }
     }
 
     @Override
@@ -1067,6 +1087,7 @@
         mStartListenersCalled = false;
         mPlayingBackwards = false;
         mReversing = false;
+        mLastFrameTime = 0;
         mCurrentIteration = 0;
         if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
             Trace.asyncTraceEnd(Trace.TRACE_TAG_VIEW, getNameForTrace(),
@@ -1176,12 +1197,13 @@
      * @hide
      */
     public final void doAnimationFrame(long frameTime) {
-        mLastFrameTime = frameTime;
         AnimationHandler handler = AnimationHandler.getInstance();
-        if (!mRunning) {
+        if (mLastFrameTime == 0) {
             // First frame
             handler.addOneShotCommitCallback(this);
-            startAnimation();
+            if (mStartDelay > 0) {
+                startAnimation();
+            }
             if (mSeekFraction < 0) {
                 mStartTime = frameTime;
             } else {
@@ -1191,6 +1213,7 @@
             }
             mStartTimeCommitted = false; // allow start time to be compensated for jank
         }
+        mLastFrameTime = frameTime;
         if (mPaused) {
             if (mPauseTime < 0) {
                 mPauseTime = frameTime;
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index f60250c..d47c0aa 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -687,9 +687,15 @@
 
     /** @hide Task isn't finished when activity is finished */
     public static final int DONT_FINISH_TASK_WITH_ACTIVITY = 0;
-    /** @hide Task is finished if the finishing activity is the root of the task */
+    /**
+     * @hide Task is finished if the finishing activity is the root of the task. To preserve the
+     * past behavior the task is also removed from recents.
+     */
     public static final int FINISH_TASK_WITH_ROOT_ACTIVITY = 1;
-    /** @hide Task is finished along with the finishing activity*/
+    /**
+     * @hide Task is finished along with the finishing activity, but it is not removed from
+     * recents.
+     */
     public static final int FINISH_TASK_WITH_ACTIVITY = 2;
 
     static final String FRAGMENTS_TAG = "android:fragments";
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 6606f9b..61a9a84 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -468,36 +468,51 @@
      */
     public static final int DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT = 1;
 
-
     /**
      * Input parameter to {@link android.app.IActivityManager#resizeTask} which indicates
-     * that the resize is from the window manager (instead of the user).
+     * that the resize doesn't need to preserve the window, and can be skipped if bounds
+     * is unchanged. This mode is used by window manager in most cases.
      * @hide
      */
     public static final int RESIZE_MODE_SYSTEM = 0;
 
     /**
      * Input parameter to {@link android.app.IActivityManager#resizeTask} which indicates
-     * that the resize is from the window manager (instead of the user) due to a screen
-     * rotation change.
+     * that the resize should preserve the window if possible.
      * @hide
      */
-    public static final int RESIZE_MODE_SYSTEM_SCREEN_ROTATION = 1;
-
-    /**
-     * Input parameter to {@link android.app.IActivityManager#resizeTask} which indicates
-     * that the resize is initiated by the user (most likely via a drag action on the
-     * window's edge or corner).
-     * @hide
-     */
-    public static final int RESIZE_MODE_USER   = 2;
+    public static final int RESIZE_MODE_PRESERVE_WINDOW   = (0x1 << 0);
 
     /**
      * Input parameter to {@link android.app.IActivityManager#resizeTask} which indicates
      * that the resize should be performed even if the bounds appears unchanged.
      * @hide
      */
-    public static final int RESIZE_MODE_FORCED = 3;
+    public static final int RESIZE_MODE_FORCED = (0x1 << 1);
+
+    /**
+     * Input parameter to {@link android.app.IActivityManager#resizeTask} used by window
+     * manager during a screen rotation.
+     * @hide
+     */
+    public static final int RESIZE_MODE_SYSTEM_SCREEN_ROTATION = RESIZE_MODE_PRESERVE_WINDOW;
+
+    /**
+     * Input parameter to {@link android.app.IActivityManager#resizeTask} used when the
+     * resize is due to a drag action.
+     * @hide
+     */
+    public static final int RESIZE_MODE_USER = RESIZE_MODE_PRESERVE_WINDOW;
+
+    /**
+     * Input parameter to {@link android.app.IActivityManager#resizeTask} which indicates
+     * that the resize should preserve the window if possible, and should not be skipped
+     * even if the bounds is unchanged. Usually used to force a resizing when a drag action
+     * is ending.
+     * @hide
+     */
+    public static final int RESIZE_MODE_USER_FORCED =
+            RESIZE_MODE_PRESERVE_WINDOW | RESIZE_MODE_FORCED;
 
     /** @hide */
     public int getFrontActivityScreenCompatMode() {
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 40eb799..373a23f 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -55,4 +55,13 @@
      * @param userId ID of the user or {@link android.os.UserHandle#USER_ALL}
      */
     public abstract ComponentName getHomeActivityForUser(int userId);
+
+    /**
+     * Called when a user has been deleted. This can happen during normal device usage
+     * or just at startup, when partially removed users are purged. Any state persisted by the
+     * ActivityManager should be purged now.
+     *
+     * @param userId The user being cleaned up.
+     */
+    public abstract void onUserRemoved(int userId);
 }
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index cb1a89f..faa3a43 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -330,9 +330,11 @@
         case START_ACTIVITY_FROM_RECENTS_TRANSACTION:
         {
             data.enforceInterface(IActivityManager.descriptor);
-            int taskId = data.readInt();
-            Bundle options = data.readInt() == 0 ? null : Bundle.CREATOR.createFromParcel(data);
-            int result = startActivityFromRecents(taskId, options);
+            final int taskId = data.readInt();
+            final int launchStackId = data.readInt();
+            final Bundle options =
+                    data.readInt() == 0 ? null : Bundle.CREATOR.createFromParcel(data);
+            final int result = startActivityFromRecents(taskId, launchStackId, options);
             reply.writeNoException();
             reply.writeInt(result);
             return true;
@@ -2689,6 +2691,13 @@
             reply.writeNoException();
             return true;
         }
+        case REMOVE_STACK: {
+            data.enforceInterface(IActivityManager.descriptor);
+            final int stackId = data.readInt();
+            removeStack(stackId);
+            reply.writeNoException();
+            return true;
+        }
         }
 
         return super.onTransact(code, data, reply, flags);
@@ -2984,11 +2993,13 @@
         data.recycle();
         return result != 0;
     }
-    public int startActivityFromRecents(int taskId, Bundle options) throws RemoteException {
+    public int startActivityFromRecents(int taskId, int launchStackId, Bundle options)
+            throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
         data.writeInterfaceToken(IActivityManager.descriptor);
         data.writeInt(taskId);
+        data.writeInt(launchStackId);
         if (options == null) {
             data.writeInt(0);
         } else {
@@ -6235,5 +6246,17 @@
         reply.recycle();
     }
 
+    @Override
+    public void removeStack(int stackId) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeInt(stackId);
+        mRemote.transact(REMOVE_STACK, data, reply, 0);
+        reply.readException();
+        data.recycle();
+        reply.recycle();
+    }
+
     private IBinder mRemote;
 }
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 4b8efab..9f24de8 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -3956,13 +3956,12 @@
                 }
                 IBinder wtoken = v.getWindowToken();
                 if (r.activity.mWindowAdded) {
-                    boolean reuseForResize = r.window.hasNonClientDecorView() && r.mPreserveWindow;
-                    if (r.onlyLocalRequest || reuseForResize) {
+                    if (r.onlyLocalRequest || r.mPreserveWindow) {
                         // Hold off on removing this until the new activity's
                         // window is being added.
                         r.mPendingRemoveWindow = r.window;
                         r.mPendingRemoveWindowManager = wm;
-                        if (reuseForResize) {
+                        if (r.mPreserveWindow) {
                             // We can only keep the part of the view hierarchy that we control,
                             // everything else must be removed, because it might not be able to
                             // behave properly when activity is relaunching.
diff --git a/core/java/android/app/AutomaticZenRule.java b/core/java/android/app/AutomaticZenRule.java
index fea5624..e5fa02b 100644
--- a/core/java/android/app/AutomaticZenRule.java
+++ b/core/java/android/app/AutomaticZenRule.java
@@ -33,6 +33,8 @@
     private int interruptionFilter;
     private Uri conditionId;
     private ComponentName owner;
+    private String id;
+    private long creationTime;
 
     /**
      * Creates an automatic zen rule.
@@ -55,6 +57,17 @@
         this.enabled = enabled;
     }
 
+    /**
+     * @SystemApi
+     * @hide
+     */
+    public AutomaticZenRule(String name, ComponentName owner, Uri conditionId,
+            int interruptionFilter, boolean enabled, String id, long creationTime) {
+        this(name, owner, conditionId, interruptionFilter, enabled);
+        this.id = id;
+        this.creationTime = creationTime;
+    }
+
     public AutomaticZenRule(Parcel source) {
         enabled = source.readInt() == 1;
         if (source.readInt() == 1) {
@@ -63,6 +76,10 @@
         interruptionFilter = source.readInt();
         conditionId = source.readParcelable(null);
         owner = source.readParcelable(null);
+        if (source.readInt() == 1) {
+            id = source.readString();
+        }
+        creationTime = source.readLong();
     }
 
     /**
@@ -101,6 +118,20 @@
     }
 
     /**
+     * Returns the wall time in milliseconds when this rule was created, if known.
+     */
+    public long getCreationTime() {
+      return creationTime;
+    }
+
+    /**
+     * Returns the unique identifier for this rule.
+     */
+    public String getId() {
+      return id;
+    }
+
+    /**
      * Sets the representation of the state that causes this rule to become active.
      */
     public void setConditionId(Uri conditionId) {
@@ -146,6 +177,13 @@
         dest.writeInt(interruptionFilter);
         dest.writeParcelable(conditionId, 0);
         dest.writeParcelable(owner, 0);
+        if (id != null) {
+            dest.writeInt(1);
+            dest.writeString(id);
+        } else {
+            dest.writeInt(0);
+        }
+        dest.writeLong(creationTime);
     }
 
     @Override
@@ -156,6 +194,8 @@
                 .append(",interruptionFilter=").append(interruptionFilter)
                 .append(",conditionId=").append(conditionId)
                 .append(",owner=").append(owner)
+                .append(",id=").append(id)
+                .append(",creationTime=").append(creationTime)
                 .append(']').toString();
     }
 
@@ -168,12 +208,14 @@
                 && Objects.equals(other.name, name)
                 && other.interruptionFilter == interruptionFilter
                 && Objects.equals(other.conditionId, conditionId)
-                && Objects.equals(other.owner, owner);
+                && Objects.equals(other.owner, owner)
+                && Objects.equals(other.id, id)
+                && other.creationTime == creationTime;
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(enabled, name, interruptionFilter, conditionId, owner);
+        return Objects.hash(enabled, name, interruptionFilter, conditionId, owner, id, creationTime);
     }
 
     public static final Parcelable.Creator<AutomaticZenRule> CREATOR
@@ -187,4 +229,4 @@
             return new AutomaticZenRule[size];
         }
     };
-}
\ No newline at end of file
+}
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 478fdd1..e23620d 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -16,8 +16,8 @@
 
 package android.app;
 
-import android.app.ActivityManager.RunningTaskInfo;
 import android.app.ActivityManager.RunningServiceInfo;
+import android.app.ActivityManager.RunningTaskInfo;
 import android.app.ActivityManager.StackInfo;
 import android.app.assist.AssistContent;
 import android.app.assist.AssistStructure;
@@ -92,7 +92,8 @@
             int userId) throws RemoteException;
     public boolean startNextMatchingActivity(IBinder callingActivity,
             Intent intent, Bundle options) throws RemoteException;
-    public int startActivityFromRecents(int taskId, Bundle options) throws RemoteException;
+    public int startActivityFromRecents(int taskId, int launchStackId, Bundle options)
+            throws RemoteException;
     public boolean finishActivity(IBinder token, int code, Intent data, int finishTask)
             throws RemoteException;
     public void finishSubActivity(IBinder token, String resultWho, int requestCode) throws RemoteException;
@@ -537,6 +538,8 @@
 
     public void suppressResizeConfigChanges(boolean suppress) throws RemoteException;
 
+    public void removeStack(int stackId) throws RemoteException;
+
     /*
      * Private non-Binder interfaces
      */
@@ -894,4 +897,5 @@
     int REPORT_SIZE_CONFIGURATIONS = IBinder.FIRST_CALL_TRANSACTION + 345;
     int MOVE_TASK_TO_DOCKED_STACK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 346;
     int SUPPRESS_RESIZE_CONFIG_CHANGES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 347;
+    int REMOVE_STACK = IBinder.FIRST_CALL_TRANSACTION + 348;
 }
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 920fbe9..e95a35a 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -93,11 +93,11 @@
     String[] getPackagesRequestingNotificationPolicyAccess();
     boolean isNotificationPolicyAccessGrantedForPackage(String pkg);
     void setNotificationPolicyAccessGranted(String pkg, boolean granted);
-    AutomaticZenRule getAutomaticZenRule(String name);
+    AutomaticZenRule getAutomaticZenRule(String id);
     List<AutomaticZenRule> getAutomaticZenRules();
-    boolean addOrUpdateAutomaticZenRule(in AutomaticZenRule automaticZenRule);
-    boolean renameAutomaticZenRule(String oldName, String newName);
-    boolean removeAutomaticZenRule(String name);
+    AutomaticZenRule addAutomaticZenRule(in AutomaticZenRule automaticZenRule);
+    boolean updateAutomaticZenRule(in AutomaticZenRule automaticZenRule);
+    boolean removeAutomaticZenRule(String id);
 
     byte[] getBackupPayload(int user);
     void applyRestore(in byte[] payload, int user);
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index cbf198b..cb0ff33 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -436,48 +436,47 @@
     }
 
     /**
-     * Returns the AutomaticZenRule with the given name, if it exists and the caller has access.
+     * Returns the AutomaticZenRule with the given id, if it exists and the caller has access.
      *
      * <p>
      * Only available if policy access is granted to this package.
      * See {@link #isNotificationPolicyAccessGranted}.
      *
      * <p>
-     * Returns null if there are no zen rules that match the given name, or if the calling package
+     * Returns null if there are no zen rules that match the given id, or if the calling package
      * doesn't own the matching rule. See {@link AutomaticZenRule#getOwner}.
      */
-    public AutomaticZenRule getAutomaticZenRule(String name) {
+    public AutomaticZenRule getAutomaticZenRule(String id) {
         INotificationManager service = getService();
         try {
-            return service.getAutomaticZenRule(name);
+            return service.getAutomaticZenRule(id);
         } catch (RemoteException e) {
         }
         return null;
     }
 
     /**
-     * Creates or updates the given zen rule.
+     * Creates the given zen rule.
      *
      * <p>
      * Only available if policy access is granted to this package.
      * See {@link #isNotificationPolicyAccessGranted}.
      *
-     * <p>
-     * Callers can only update rules that they own. See {@link AutomaticZenRule#getOwner}.
-     * @param automaticZenRule the rule to create or update.
-     * @return Whether the rule was successfully created or updated.
+     * @param automaticZenRule the rule to create.
+     * @return A fully populated {@link AutomaticZenRule} if the rule was persisted successfully,
+     * null otherwise.
      */
-    public boolean addOrUpdateAutomaticZenRule(AutomaticZenRule automaticZenRule) {
+    public AutomaticZenRule addAutomaticZenRule(AutomaticZenRule automaticZenRule) {
         INotificationManager service = getService();
         try {
-            return service.addOrUpdateAutomaticZenRule(automaticZenRule);
+            return service.addAutomaticZenRule(automaticZenRule);
         } catch (RemoteException e) {
         }
-        return false;
+        return null;
     }
 
     /**
-     * Renames a zen rule.
+     * Updates the given zen rule.
      *
      * <p>
      * Only available if policy access is granted to this package.
@@ -485,21 +484,20 @@
      *
      * <p>
      * Callers can only update rules that they own. See {@link AutomaticZenRule#getOwner}.
-     * @param oldName The name of the rule to update.
-     * @param newName The new name for the rule.
+     * @param automaticZenRule the rule to update. 
      * @return Whether the rule was successfully updated.
      */
-    public boolean renameAutomaticZenRule(String oldName, String newName) {
+    public boolean updateAutomaticZenRule(AutomaticZenRule automaticZenRule) {
         INotificationManager service = getService();
         try {
-            return service.renameAutomaticZenRule(oldName, newName);
+            return service.updateAutomaticZenRule(automaticZenRule);
         } catch (RemoteException e) {
         }
         return false;
     }
 
     /**
-     * Deletes the automatic zen rule with the given name.
+     * Deletes the automatic zen rule with the given id.
      *
      * <p>
      * Only available if policy access is granted to this package.
@@ -507,13 +505,13 @@
      *
      * <p>
      * Callers can only delete rules that they own. See {@link AutomaticZenRule#getOwner}.
-     * @param name the name of the rule to delete.
+     * @param id the id of the rule to delete.
      * @return Whether the rule was successfully deleted.
      */
-    public boolean removeAutomaticZenRule(String name) {
+    public boolean removeAutomaticZenRule(String id) {
         INotificationManager service = getService();
         try {
-            return service.removeAutomaticZenRule(name);
+            return service.removeAutomaticZenRule(id);
         } catch (RemoteException e) {
         }
         return false;
diff --git a/core/java/android/app/admin/DeviceAdminReceiver.java b/core/java/android/app/admin/DeviceAdminReceiver.java
index a1bb40c..84b6d39 100644
--- a/core/java/android/app/admin/DeviceAdminReceiver.java
+++ b/core/java/android/app/admin/DeviceAdminReceiver.java
@@ -227,24 +227,6 @@
     public static final String ACTION_PROFILE_PROVISIONING_COMPLETE =
             "android.app.action.PROFILE_PROVISIONING_COMPLETE";
 
-    /**
-     * @hide
-     * Broadcast Action: This broadcast is sent to indicate that the system is ready for the device
-     * initializer to perform user setup tasks. This is only applicable to devices managed by a
-     * device owner app.
-     *
-     * <p>The broadcast will be limited to the {@link DeviceAdminReceiver} component specified in
-     * the device initializer field of the original intent or NFC bump that started the provisioning
-     * process. You will generally handle this in
-     * {@link DeviceAdminReceiver#onReadyForUserInitialization}.
-     *
-     * <p>Input: Nothing.</p>
-     * <p>Output: Nothing</p>
-     */
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_READY_FOR_USER_INITIALIZATION =
-            "android.app.action.READY_FOR_USER_INITIALIZATION";
-
     /** @hide */
     public static final String ACTION_CHOOSE_PRIVATE_KEY_ALIAS = "android.app.action.CHOOSE_PRIVATE_KEY_ALIAS";
 
@@ -435,23 +417,13 @@
 
     /**
      * Called during provisioning of a managed device to allow the device initializer to perform
-     * user setup steps. Only device initializers should override this method.
-     *
-     * <p> Called when the DeviceAdminReceiver receives an
-     * android.app.action.ACTION_READY_FOR_USER_INITIALIZATION broadcast. As a prerequisite for the
-     * execution of this callback the {@link DeviceAdminReceiver} has
-     * to declare an intent filter for android.app.action.ACTION_READY_FOR_USER_INITIALIZATION. Only
-     * the component specified in the device initializer component name field of the
-     * original intent or NFC bump that started the provisioning process will receive this callback.
-     *
-     * <p>It is not assumed that the device initializer is finished when it returns from
-     * this call, as it may do additional setup asynchronously. The device initializer must enable
-     * the current user when it has finished any additional setup (such as adding an account by
-     * using the {@link AccountManager}) in order for the user to be functional.
+     * user setup steps.
      *
      * @param context The running context as per {@link #onReceive}.
      * @param intent The received intent as per {@link #onReceive}.
+     * @deprecated Do not use
      */
+    @Deprecated
     @SystemApi
     public void onReadyForUserInitialization(Context context, Intent intent) {
     }
@@ -549,8 +521,6 @@
             onLockTaskModeEntering(context, intent, pkg);
         } else if (ACTION_LOCK_TASK_EXITING.equals(action)) {
             onLockTaskModeExiting(context, intent);
-        } else if (ACTION_READY_FOR_USER_INITIALIZATION.equals(action)) {
-            onReadyForUserInitialization(context, intent);
         } else if (ACTION_NOTIFY_PENDING_SYSTEM_UPDATE.equals(action)) {
             long receivedTime = intent.getLongExtra(EXTRA_SYSTEM_UPDATE_RECEIVED_TIME, -1);
             onSystemUpdatePending(context, intent, receivedTime);
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index a118f16..b45f3b4 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -497,96 +497,6 @@
              "android.app.extra.PROVISIONING_SKIP_ENCRYPTION";
 
     /**
-     * @hide
-     * On devices managed by a device owner app, a {@link ComponentName} extra indicating the
-     * component of the application that is temporarily granted device owner privileges during
-     * device initialization and profile owner privileges during secondary user initialization.
-     *
-     * <p>
-     * It can also be used in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC_V2} that starts
-     * device owner provisioning via an NFC bump. For the NFC record, it should be flattened to a
-     * string first.
-     *
-     * @see ComponentName#flattenToShortString()
-     */
-    public static final String EXTRA_PROVISIONING_DEVICE_INITIALIZER_COMPONENT_NAME
-        = "android.app.extra.PROVISIONING_DEVICE_INITIALIZER_COMPONENT_NAME";
-
-    /**
-     * @hide
-     * A String extra holding an http url that specifies the download location of the device
-     * initializer package. When not provided it is assumed that the device initializer package is
-     * already installed.
-     *
-     * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC_V2} that starts device owner
-     * provisioning via an NFC bump.
-     */
-    public static final String EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_LOCATION
-        = "android.app.extra.PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_LOCATION";
-
-    /**
-     * @hide
-     * An int extra holding a minimum required version code for the device initializer package.
-     * If the initializer is already installed on the device, it will only be re-downloaded from
-     * {@link #EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_LOCATION} if the version of
-     * the installed package is less than this version code.
-     *
-     * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC_V2} that starts device owner
-     * provisioning via an NFC bump.
-     */
-    public static final String EXTRA_PROVISIONING_DEVICE_INITIALIZER_MINIMUM_VERSION_CODE
-        = "android.app.extra.PROVISIONING_DEVICE_INITIALIZER_MINIMUM_VERSION_CODE";
-
-    /**
-     * @hide
-     * A String extra holding a http cookie header which should be used in the http request to the
-     * url specified in {@link #EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_LOCATION}.
-     *
-     * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC_V2} that starts device owner
-     * provisioning via an NFC bump.
-     */
-    public static final String EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_COOKIE_HEADER
-        = "android.app.extra.PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_COOKIE_HEADER";
-
-    /**
-     * @hide
-     * A String extra holding the URL-safe base64 encoded SHA-256 checksum of the file at download
-     * location specified in
-     * {@link #EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_LOCATION}.
-     *
-     * <p>Either this extra or {@link #EXTRA_PROVISIONING_DEVICE_INITIALIZER_SIGNATURE_CHECKSUM}
-     * should be present. The provided checksum should match the checksum of the file at the
-     * download location. If the checksum doesn't match an error will be shown to the user and the
-     * user will be asked to factory reset the device.
-     *
-     * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC_V2} that starts device owner
-     * provisioning via an NFC bump.
-     */
-    public static final String EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_CHECKSUM
-        = "android.app.extra.PROVISIONING_DEVICE_INITIALIZER_PACKAGE_CHECKSUM";
-
-    /**
-     * @hide
-     * A String extra holding the URL-safe base64 encoded SHA-256 checksum of any signature of the
-     * android package archive at the download location specified in {@link
-     * #EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_LOCATION}.
-     *
-     * <p>The signatures of an android package archive can be obtained using
-     * {@link android.content.pm.PackageManager#getPackageArchiveInfo} with flag
-     * {@link android.content.pm.PackageManager#GET_SIGNATURES}.
-     *
-     * <p>Either this extra or {@link #EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_CHECKSUM}
-     * should be present. The provided checksum should match the checksum of any signature of the
-     * file at the download location. If the checksum doesn't match an error will be shown to the
-     * user and the user will be asked to factory reset the device.
-     *
-     * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC_V2} that starts device owner
-     * provisioning via an NFC bump.
-     */
-    public static final String EXTRA_PROVISIONING_DEVICE_INITIALIZER_SIGNATURE_CHECKSUM
-        = "android.app.extra.PROVISIONING_DEVICE_INITIALIZER_SIGNATURE_CHECKSUM";
-
-    /**
      * This MIME type is used for starting the Device Owner provisioning.
      *
      * <p>During device owner provisioning a device admin app is set as the owner of the device.
@@ -629,44 +539,6 @@
     public static final String MIME_TYPE_PROVISIONING_NFC
         = "application/com.android.managedprovisioning";
 
-
-    /**
-     * @hide
-     * This MIME type is used for starting the Device Owner provisioning that requires
-     * new provisioning features introduced in API version
-     * {@link android.os.Build.VERSION_CODES#M} in addition to those supported in earlier
-     * versions.
-     *
-     * <p>During device owner provisioning a device admin app is set as the owner of the device.
-     * A device owner has full control over the device. The device owner can not be modified by the
-     * user.
-     *
-     * <p> A typical use case would be a device that is owned by a company, but used by either an
-     * employee or client.
-     *
-     * <p> The NFC message should be sent to an unprovisioned device.
-     *
-     * <p>The NFC record must contain a serialized {@link java.util.Properties} object which
-     * contains the following properties in addition to properties listed at
-     * {@link #MIME_TYPE_PROVISIONING_NFC}:
-     * <ul>
-     * <li>{@link #EXTRA_PROVISIONING_SKIP_ENCRYPTION}, optional</li>
-     * <li>{@link #EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME}.
-     * Replaces {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME}. The value of the property
-     * should be converted to a String via
-     * {@link android.content.ComponentName#flattenToString()}</li>
-     * <li>{@link #EXTRA_PROVISIONING_DEVICE_ADMIN_MINIMUM_VERSION_CODE}, optional</li></ul>
-     *
-     * <p> When device owner provisioning has completed, an intent of the type
-     * {@link DeviceAdminReceiver#ACTION_PROFILE_PROVISIONING_COMPLETE} is broadcasted to the
-     * device owner.
-     *
-     * <p>
-     * If provisioning fails, the device is factory reset.
-     */
-    public static final String MIME_TYPE_PROVISIONING_NFC_V2
-            = "application/com.android.managedprovisioning.v2";
-
     /**
      * Activity action: ask the user to add a new device administrator to the system.
      * The desired policy is the ComponentName of the policy in the
@@ -2701,28 +2573,28 @@
     /**
      * @hide
      * Sets the given package as the device owner.
-     * Same as {@link #setDeviceOwner(String, String)} but without setting a device owner name.
-     * @param packageName the package name of the application to be registered as the device owner.
+     * Same as {@link #setDeviceOwner(ComponentName, String)} but without setting a device owner name.
+     * @param who the component name to be registered as device owner.
      * @return whether the package was successfully registered as the device owner.
      * @throws IllegalArgumentException if the package name is null or invalid
      * @throws IllegalStateException If the preconditions mentioned are not met.
      */
-    public boolean setDeviceOwner(String packageName) {
-        return setDeviceOwner(packageName, null);
+    public boolean setDeviceOwner(ComponentName who) {
+        return setDeviceOwner(who, null);
     }
 
     /**
      * @hide
      */
-    public boolean setDeviceOwner(String packageName, int userId)  {
-        return setDeviceOwner(packageName, null, userId);
+    public boolean setDeviceOwner(ComponentName who, int userId)  {
+        return setDeviceOwner(who, null, userId);
     }
 
     /**
      * @hide
      */
-    public boolean setDeviceOwner(String packageName, String ownerName) {
-        return setDeviceOwner(packageName, ownerName, UserHandle.USER_SYSTEM);
+    public boolean setDeviceOwner(ComponentName who, String ownerName) {
+        return setDeviceOwner(who, ownerName, UserHandle.USER_SYSTEM);
     }
 
     /**
@@ -2733,18 +2605,18 @@
      * this method.
      * Calling this after the setup phase of the primary user has completed is allowed only if
      * the caller is the shell uid, and there are no additional users and no accounts.
-     * @param packageName the package name of the application to be registered as the device owner.
+     * @param who the component name to be registered as device owner.
      * @param ownerName the human readable name of the institution that owns this device.
      * @param userId ID of the user on which the device owner runs.
      * @return whether the package was successfully registered as the device owner.
      * @throws IllegalArgumentException if the package name is null or invalid
      * @throws IllegalStateException If the preconditions mentioned are not met.
      */
-    public boolean setDeviceOwner(String packageName, String ownerName, int userId)
+    public boolean setDeviceOwner(ComponentName who, String ownerName, int userId)
             throws IllegalArgumentException, IllegalStateException {
         if (mService != null) {
             try {
-                return mService.setDeviceOwner(packageName, ownerName, userId);
+                return mService.setDeviceOwner(who, ownerName, userId);
             } catch (RemoteException re) {
                 Log.w(TAG, "Failed to set device owner");
             }
@@ -2763,14 +2635,15 @@
      * the setup process.
      * @param packageName the package name of the app, to compare with the registered device owner
      * app, if any.
-     * @return whether or not the package is registered as the device owner app.
+     * @return whether or not the package is registered as the device owner app.  Note this method
+     * does *not* check weather the device owner is actually running on the current user.
      */
     public boolean isDeviceOwnerApp(String packageName) {
         if (mService != null) {
             try {
-                return mService.isDeviceOwner(packageName);
-            } catch (RemoteException re) {
-                Log.w(TAG, "Failed to check device owner");
+                return mService.isDeviceOwnerPackage(packageName);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed talking with device policy service", e);
             }
         }
         return false;
@@ -2785,6 +2658,17 @@
     }
 
     /**
+     * Check whether a given component is registered as a device owner.
+     * Note this method does *not* check weather the device owner is actually running on the current
+     * user.
+     *
+     * @hide
+     */
+    public boolean isDeviceOwner(ComponentName who) {
+        return (who != null) && who.equals(getDeviceOwner());
+    }
+
+    /**
      * Clears the current device owner.  The caller must be the device owner.
      *
      * This function should be used cautiously as once it is called it cannot
@@ -2803,20 +2687,24 @@
         }
     }
 
-    /** @hide */
+    /**
+     * Returns the device owner package name.  Note this method will still return the device owner
+     * package name even if it's running on a different user.
+     *
+     * @hide
+     */
     @SystemApi
     public String getDeviceOwner() {
-        if (mService != null) {
-            try {
-                return mService.getDeviceOwner();
-            } catch (RemoteException re) {
-                Log.w(TAG, "Failed to get device owner");
-            }
-        }
-        return null;
+        final ComponentName componentName = getDeviceOwnerComponent();
+        return componentName == null ? null : componentName.getPackageName();
     }
 
-    /** @hide */
+    /**
+     * Returns the device owner name.  Note this method will still return the device owner
+     * name even if it's running on a different user.
+     *
+     * @hide
+     */
     public String getDeviceOwnerName() {
         if (mService != null) {
             try {
@@ -2829,135 +2717,44 @@
     }
 
     /**
+     * Returns the device owner component name.  Note this method will still return the device owner
+     * component name even if it's running on a different user.
+     *
      * @hide
-     * Sets the given component as the device initializer. The package must already be installed and
-     * set as an active device administrator, and there must not be an existing device initializer,
-     * for this call to succeed. This method can only be called by an app holding the
-     * MANAGE_DEVICE_ADMINS permission before the device is provisioned or by a device owner app. A
-     * device initializer app is granted device owner privileges during device initialization and
-     * profile owner privileges during secondary user initialization.
-     * @param admin Which {@link DeviceAdminReceiver} this request is associated with, or
-     *              {@code null} if not called by the device owner.
-     * @param initializer Which {@link DeviceAdminReceiver} to make device initializer.
-     * @return whether the component was successfully registered as the device initializer.
-     * @throws IllegalArgumentException if the componentname is null or invalid
-     * @throws IllegalStateException if the caller is not device owner or the device has
-     *         already been provisioned or a device initializer already exists.
      */
-    public boolean setDeviceInitializer(@Nullable ComponentName admin,
-            @NonNull ComponentName initializer)
-            throws IllegalArgumentException, IllegalStateException {
+    public ComponentName getDeviceOwnerComponent() {
         if (mService != null) {
             try {
-                return mService.setDeviceInitializer(admin, initializer);
+                return mService.getDeviceOwner();
             } catch (RemoteException re) {
-                Log.w(TAG, "Failed to set device initializer");
+                Log.w(TAG, "Failed to get device owner");
             }
         }
-        return false;
+        return null;
     }
 
     /**
      * @hide
-     * Used to determine if a particular package has been registered as the device initializer.
-     *
-     * @param packageName the package name of the app, to compare with the registered device
-     *        initializer app, if any.
-     * @return whether or not the caller is registered as the device initializer app.
+     * @deprecated Do not use
+     * @removed
      */
-    public boolean isDeviceInitializerApp(String packageName) {
-        if (mService != null) {
-            try {
-                return mService.isDeviceInitializer(packageName);
-            } catch (RemoteException re) {
-                Log.w(TAG, "Failed to check device initializer");
-            }
-        }
-        return false;
-    }
-
-    /**
-     * @hide
-     * Removes the device initializer, so that it will not be invoked on user initialization for any
-     * subsequently created users. This method can be called by either the device owner or device
-     * initializer itself. The caller must be an active administrator.
-     *
-     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
-     */
-    public void clearDeviceInitializerApp(@NonNull ComponentName admin) {
-        if (mService != null) {
-            try {
-                mService.clearDeviceInitializer(admin);
-            } catch (RemoteException re) {
-                Log.w(TAG, "Failed to clear device initializer");
-            }
-        }
-    }
-
-    /**
-     * @hide
-     * Gets the device initializer of the system.
-     *
-     * @return the package name of the device initializer.
-     */
+    @Deprecated
     @SystemApi
     public String getDeviceInitializerApp() {
-        if (mService != null) {
-            try {
-                return mService.getDeviceInitializer();
-            } catch (RemoteException re) {
-                Log.w(TAG, "Failed to get device initializer");
-            }
-        }
         return null;
     }
 
     /**
      * @hide
-     * Gets the device initializer component of the system.
-     *
-     * @return the component name of the device initializer.
+     * @deprecated Do not use
+     * @removed
      */
+    @Deprecated
     @SystemApi
     public ComponentName getDeviceInitializerComponent() {
-        if (mService != null) {
-            try {
-                return mService.getDeviceInitializerComponent();
-            } catch (RemoteException re) {
-                Log.w(TAG, "Failed to get device initializer");
-            }
-        }
         return null;
     }
 
-
-    /**
-     * @hide
-     * Sets the enabled state of the user. A user should be enabled only once it is ready to
-     * be used.
-     *
-     * <p>Device initializer must call this method to mark the user as functional.
-     * Only the device initializer agent can call this.
-     *
-     * <p>When the user is enabled, if the device initializer is not also the device owner, the
-     * device initializer will no longer have elevated permissions to call methods in this class.
-     * Additionally, it will be removed as an active administrator and its
-     * {@link DeviceAdminReceiver} will be disabled.
-     *
-     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
-     * @return whether the user is now enabled.
-     */
-    public boolean setUserEnabled(@NonNull ComponentName admin) {
-        if (mService != null) {
-            try {
-                return mService.setUserEnabled(admin);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
-            }
-        }
-        return false;
-    }
-
     /**
      * @hide
      * @deprecated Use #ACTION_SET_PROFILE_OWNER
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 55a21c6..ccaa8cb 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -113,9 +113,9 @@
     void reportFailedPasswordAttempt(int userHandle);
     void reportSuccessfulPasswordAttempt(int userHandle);
 
-    boolean setDeviceOwner(String packageName, String ownerName, int userId);
-    boolean isDeviceOwner(String packageName);
-    String getDeviceOwner();
+    boolean setDeviceOwner(in ComponentName who, String ownerName, int userId);
+    boolean isDeviceOwnerPackage(String packageName);
+    ComponentName getDeviceOwner();
     String getDeviceOwnerName();
     void clearDeviceOwner(String packageName);
 
@@ -211,13 +211,6 @@
 
     boolean isRemovingAdmin(in ComponentName adminReceiver, int userHandle);
 
-    boolean setUserEnabled(in ComponentName who);
-    boolean isDeviceInitializer(String packageName);
-    void clearDeviceInitializer(in ComponentName who);
-    boolean setDeviceInitializer(in ComponentName who, in ComponentName initializer);
-    String getDeviceInitializer();
-    ComponentName getDeviceInitializerComponent();
-
     void setUserIcon(in ComponentName admin, in Bitmap icon);
 
     void setSystemUpdatePolicy(in ComponentName who, in SystemUpdatePolicy policy);
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 9341be1..3283005 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -868,6 +868,9 @@
         public static final int MODE_INHERIT_EXISTING = 2;
 
         /** {@hide} */
+        public static final int UID_UNKNOWN = -1;
+
+        /** {@hide} */
         public int mode = MODE_INVALID;
         /** {@hide} */
         public int installFlags;
@@ -886,6 +889,8 @@
         /** {@hide} */
         public Uri originatingUri;
         /** {@hide} */
+        public int originatingUid = UID_UNKNOWN;
+        /** {@hide} */
         public Uri referrerUri;
         /** {@hide} */
         public String abiOverride;
@@ -915,6 +920,7 @@
             appIcon = source.readParcelable(null);
             appLabel = source.readString();
             originatingUri = source.readParcelable(null);
+            originatingUid = source.readInt();
             referrerUri = source.readParcelable(null);
             abiOverride = source.readString();
             volumeUuid = source.readString();
@@ -983,6 +989,15 @@
         }
 
         /**
+         * Sets the UID that initiated package installation. Used for verification purposes.
+         *
+         * @see PackageManager#EXTRA_VERIFICATION_INSTALLER_UID
+         */
+        public void setOriginatingUid(int originatingUid) {
+            this.originatingUid = originatingUid;
+        }
+
+        /**
          * Optionally set the URI that referred you to install this package. Used
          * for verification purposes.
          *
@@ -1022,6 +1037,11 @@
         }
 
         /** {@hide} */
+        public void setInstallFlagsForcePermissionPrompt() {
+            installFlags |= PackageManager.INSTALL_FORCE_PERMISSION_PROMPT;
+        }
+
+        /** {@hide} */
         public void dump(IndentingPrintWriter pw) {
             pw.printPair("mode", mode);
             pw.printHexPair("installFlags", installFlags);
@@ -1031,6 +1051,7 @@
             pw.printPair("appIcon", (appIcon != null));
             pw.printPair("appLabel", appLabel);
             pw.printPair("originatingUri", originatingUri);
+            pw.printPair("originatingUid", originatingUid);
             pw.printPair("referrerUri", referrerUri);
             pw.printPair("abiOverride", abiOverride);
             pw.printPair("volumeUuid", volumeUuid);
@@ -1053,6 +1074,7 @@
             dest.writeParcelable(appIcon, flags);
             dest.writeString(appLabel);
             dest.writeParcelable(originatingUri, flags);
+            dest.writeInt(originatingUid);
             dest.writeParcelable(referrerUri, flags);
             dest.writeString(abiOverride);
             dest.writeString(volumeUuid);
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 697b946..82cbbbe 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -422,6 +422,15 @@
     public static final int INSTALL_FORCE_VOLUME_UUID = 0x00000200;
 
     /**
+     * Flag parameter for {@link #installPackage} to indicate that we always want to force
+     * the prompt for permission approval. This overrides any special behaviour for internal
+     * components.
+     *
+     * @hide
+     */
+    public static final int INSTALL_FORCE_PERMISSION_PROMPT = 0x00000400;
+
+    /**
      * Flag parameter for
      * {@link #setComponentEnabledSetting(android.content.ComponentName, int, int)} to indicate
      * that you don't want to kill the app containing the component.  Be careful when you set this
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 04b1a3b..1a8602b 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -3201,15 +3201,12 @@
             }
 
             a.info.resizeable = sa.getBoolean(
-                    R.styleable.AndroidManifestActivity_resizeableActivity, false);
-            if (a.info.resizeable) {
-                // Fixed screen orientation isn't supported with resizeable activities.
-                a.info.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
-            } else {
-                a.info.screenOrientation = sa.getInt(
-                        R.styleable.AndroidManifestActivity_screenOrientation,
-                        ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
-            }
+                    R.styleable.AndroidManifestActivity_resizeableActivity,
+                    owner.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.N);
+
+            a.info.screenOrientation = sa.getInt(
+                    R.styleable.AndroidManifestActivity_screenOrientation,
+                    ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
 
             a.info.lockTaskLaunchMode =
                     sa.getInt(R.styleable.AndroidManifestActivity_lockTaskMode, 0);
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index 20b0be1..8f45f72 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -46,7 +46,7 @@
  * provide substantially improved capabilities over the older camera
  * API. Applications that target the limited level devices will run unchanged on
  * the full-level devices; if your application requires a full-level device for
- * proper operation, declare the "android.hardware.camera2.full" feature in your
+ * proper operation, declare the "android.hardware.camera.level.full" feature in your
  * manifest.</p>
  *
  * @see CameraManager#openCamera
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index e965d65..3f566eb 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -1545,7 +1545,7 @@
     /**
      * <p>Whether video stabilization is
      * active.</p>
-     * <p>Video stabilization automatically translates and scales images from
+     * <p>Video stabilization automatically warps images from
      * the camera in order to stabilize motion between consecutive frames.</p>
      * <p>If enabled, video stabilization can modify the
      * {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} to keep the video stream stabilized.</p>
@@ -1555,6 +1555,15 @@
      * the video stabilization modes in the first several capture results may
      * still be "OFF", and it will become "ON" when the initialization is
      * done.</p>
+     * <p>In addition, not all recording sizes or frame rates may be supported for
+     * stabilization by a device that reports stabilization support. It is guaranteed
+     * that an output targeting a MediaRecorder or MediaCodec will be stabilized if
+     * the recording resolution is less than or equal to 1920 x 1080 (width less than
+     * or equal to 1920, height less than or equal to 1080), and the recording
+     * frame rate is less than or equal to 30fps.  At other sizes, the CaptureResult
+     * {@link CaptureRequest#CONTROL_VIDEO_STABILIZATION_MODE android.control.videoStabilizationMode} field will return
+     * OFF if the recording output is not stabilized, or if there are no output
+     * Surface types that can be stabilized.</p>
      * <p>If a camera device supports both this mode and OIS
      * ({@link CaptureRequest#LENS_OPTICAL_STABILIZATION_MODE android.lens.opticalStabilizationMode}), turning both modes on may
      * produce undesirable interaction, so it is recommended not to enable
@@ -1566,6 +1575,7 @@
      * </ul></p>
      * <p>This key is available on all devices.</p>
      *
+     * @see CaptureRequest#CONTROL_VIDEO_STABILIZATION_MODE
      * @see CaptureRequest#LENS_OPTICAL_STABILIZATION_MODE
      * @see CaptureRequest#SCALER_CROP_REGION
      * @see #CONTROL_VIDEO_STABILIZATION_MODE_OFF
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index 46eddb3..b3acf2b 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -2056,7 +2056,7 @@
     /**
      * <p>Whether video stabilization is
      * active.</p>
-     * <p>Video stabilization automatically translates and scales images from
+     * <p>Video stabilization automatically warps images from
      * the camera in order to stabilize motion between consecutive frames.</p>
      * <p>If enabled, video stabilization can modify the
      * {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} to keep the video stream stabilized.</p>
@@ -2066,6 +2066,15 @@
      * the video stabilization modes in the first several capture results may
      * still be "OFF", and it will become "ON" when the initialization is
      * done.</p>
+     * <p>In addition, not all recording sizes or frame rates may be supported for
+     * stabilization by a device that reports stabilization support. It is guaranteed
+     * that an output targeting a MediaRecorder or MediaCodec will be stabilized if
+     * the recording resolution is less than or equal to 1920 x 1080 (width less than
+     * or equal to 1920, height less than or equal to 1080), and the recording
+     * frame rate is less than or equal to 30fps.  At other sizes, the CaptureResult
+     * {@link CaptureRequest#CONTROL_VIDEO_STABILIZATION_MODE android.control.videoStabilizationMode} field will return
+     * OFF if the recording output is not stabilized, or if there are no output
+     * Surface types that can be stabilized.</p>
      * <p>If a camera device supports both this mode and OIS
      * ({@link CaptureRequest#LENS_OPTICAL_STABILIZATION_MODE android.lens.opticalStabilizationMode}), turning both modes on may
      * produce undesirable interaction, so it is recommended not to enable
@@ -2077,6 +2086,7 @@
      * </ul></p>
      * <p>This key is available on all devices.</p>
      *
+     * @see CaptureRequest#CONTROL_VIDEO_STABILIZATION_MODE
      * @see CaptureRequest#LENS_OPTICAL_STABILIZATION_MODE
      * @see CaptureRequest#SCALER_CROP_REGION
      * @see #CONTROL_VIDEO_STABILIZATION_MODE_OFF
diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java
index c4de4a2..6bfa2a4 100644
--- a/core/java/android/net/LinkProperties.java
+++ b/core/java/android/net/LinkProperties.java
@@ -667,7 +667,8 @@
      * @return {@code true} if there is an IPv4 address, {@code false} otherwise.
      */
     private boolean hasIPv4AddressOnInterface(String iface) {
-        return (mIfaceName.equals(iface) && hasIPv4Address()) ||
+        // mIfaceName can be null.
+        return (Objects.equals(iface, mIfaceName) && hasIPv4Address()) ||
                 (iface != null && mStackedLinks.containsKey(iface) &&
                         mStackedLinks.get(iface).hasIPv4Address());
     }
diff --git a/core/java/android/os/BaseBundle.java b/core/java/android/os/BaseBundle.java
index c373308..cd483b1 100644
--- a/core/java/android/os/BaseBundle.java
+++ b/core/java/android/os/BaseBundle.java
@@ -19,6 +19,7 @@
 import android.annotation.Nullable;
 import android.util.ArrayMap;
 import android.util.Log;
+import android.util.MathUtils;
 
 import java.io.Serializable;
 import java.util.ArrayList;
@@ -1345,18 +1346,19 @@
      */
     void readFromParcelInner(Parcel parcel) {
         int length = parcel.readInt();
-        if (length < 0) {
-            throw new RuntimeException("Bad length in parcel: " + length);
-        }
         readFromParcelInner(parcel, length);
     }
 
     private void readFromParcelInner(Parcel parcel, int length) {
-        if (length == 0) {
+        if (length < 0) {
+            throw new RuntimeException("Bad length in parcel: " + length);
+
+        } else if (length == 0) {
             // Empty Bundle or end of data.
             mParcelledData = EMPTY_PARCEL;
             return;
         }
+
         int magic = parcel.readInt();
         if (magic != BUNDLE_MAGIC) {
             //noinspection ThrowableInstanceNeverThrown
@@ -1366,7 +1368,7 @@
 
         // Advance within this Parcel
         int offset = parcel.dataPosition();
-        parcel.setDataPosition(offset + length);
+        parcel.setDataPosition(MathUtils.addOrThrow(offset, length));
 
         Parcel p = Parcel.obtain();
         p.setDataPosition(0);
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 2374899..85041ad 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -634,6 +634,11 @@
          * M comes after L.
          */
         public static final int M = 23;
+
+        /**
+         * N is for ¯\_(ツ)_/¯.
+         */
+        public static final int N = CUR_DEVELOPMENT;
     }
 
     /** The type of build, like "user" or "eng". */
@@ -691,6 +696,9 @@
      * @hide
      */
     public static boolean isBuildConsistent() {
+        // Don't care on eng builds.  Incremental build may trigger false negative.
+        if ("eng".equals(TYPE)) return true;
+
         final String system = SystemProperties.get("ro.build.fingerprint");
         final String vendor = SystemProperties.get("ro.vendor.build.fingerprint");
         final String bootimage = SystemProperties.get("ro.bootimage.build.fingerprint");
diff --git a/core/java/android/os/ParcelableParcel.java b/core/java/android/os/ParcelableParcel.java
index 11785f1..5bbe6488 100644
--- a/core/java/android/os/ParcelableParcel.java
+++ b/core/java/android/os/ParcelableParcel.java
@@ -16,6 +16,8 @@
 
 package android.os;
 
+import android.util.MathUtils;
+
 /**
  * Parcelable containing a raw Parcel of data.
  * @hide
@@ -33,9 +35,13 @@
         mParcel = Parcel.obtain();
         mClassLoader = loader;
         int size = src.readInt();
+        if (size < 0) {
+            throw new IllegalArgumentException("Negative size read from parcel");
+        }
+
         int pos = src.dataPosition();
-        mParcel.appendFrom(src, src.dataPosition(), size);
-        src.setDataPosition(pos + size);
+        src.setDataPosition(MathUtils.addOrThrow(pos, size));
+        mParcel.appendFrom(src, pos, size);
     }
 
     public Parcel getParcel() {
diff --git a/core/java/android/preference/PreferenceFragment.java b/core/java/android/preference/PreferenceFragment.java
index 66642de..db04c71 100644
--- a/core/java/android/preference/PreferenceFragment.java
+++ b/core/java/android/preference/PreferenceFragment.java
@@ -214,6 +214,9 @@
 
     @Override
     public void onDestroyView() {
+        if (mList != null) {
+            mList.setOnKeyListener(null);
+        }
         mList = null;
         mHandler.removeCallbacks(mRequestFocus);
         mHandler.removeMessages(MSG_BIND_PREFERENCES);
diff --git a/core/java/android/preference/PreferenceGroup.java b/core/java/android/preference/PreferenceGroup.java
index d6e9e61f..13c3661 100644
--- a/core/java/android/preference/PreferenceGroup.java
+++ b/core/java/android/preference/PreferenceGroup.java
@@ -148,16 +148,15 @@
             }
         }
 
-        int insertionIndex = Collections.binarySearch(mPreferenceList, preference);
-        if (insertionIndex < 0) {
-            insertionIndex = insertionIndex * -1 - 1;
-        }
-
         if (!onPrepareAddPreference(preference)) {
             return false;
         }
 
         synchronized(this) {
+            int insertionIndex = Collections.binarySearch(mPreferenceList, preference);
+            if (insertionIndex < 0) {
+                insertionIndex = insertionIndex * -1 - 1;
+            }
             mPreferenceList.add(insertionIndex, preference);
         }
 
diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java
index 4f880b1..342f8c7 100644
--- a/core/java/android/provider/CallLog.java
+++ b/core/java/android/provider/CallLog.java
@@ -153,6 +153,18 @@
         /**
          * The type of the call (incoming, outgoing or missed).
          * <P>Type: INTEGER (int)</P>
+         *
+         * <p>
+         * Allowed values:
+         * <ul>
+         * <li>{@link #INCOMING_TYPE}</li>
+         * <li>{@link #OUTGOING_TYPE}</li>
+         * <li>{@link #MISSED_TYPE}</li>
+         * <li>{@link #VOICEMAIL_TYPE}</li>
+         * <li>{@link #REJECTED_TYPE}</li>
+         * <li>{@link #BLOCKED_TYPE}</li>
+         * </ul>
+         * </p>
          */
         public static final String TYPE = "type";
 
@@ -164,6 +176,10 @@
         public static final int MISSED_TYPE = 3;
         /** Call log type for voicemails. */
         public static final int VOICEMAIL_TYPE = 4;
+        /** Call log type for calls rejected by direct user action. */
+        public static final int REJECTED_TYPE = 5;
+        /** Call log type for calls blocked automatically. */
+        public static final int BLOCKED_TYPE = 6;
 
         /**
          * Bit-mask describing features of the call (e.g. video).
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index 1b104e3..241e6db 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -19,6 +19,7 @@
 import static android.net.TrafficStats.KB_IN_BYTES;
 import static android.system.OsConstants.SEEK_SET;
 
+import android.annotation.Nullable;
 import android.content.ContentProviderClient;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -764,19 +765,33 @@
      * @see #buildDocumentUri(String, String)
      * @see #buildDocumentUriUsingTree(Uri, String)
      */
-    public static boolean isDocumentUri(Context context, Uri uri) {
-        final List<String> paths = uri.getPathSegments();
-        if (paths.size() == 2 && PATH_DOCUMENT.equals(paths.get(0))) {
-            return isDocumentsProvider(context, uri.getAuthority());
-        }
-        if (paths.size() == 4 && PATH_TREE.equals(paths.get(0))
-                && PATH_DOCUMENT.equals(paths.get(2))) {
-            return isDocumentsProvider(context, uri.getAuthority());
+    public static boolean isDocumentUri(Context context, @Nullable Uri uri) {
+        if (isContentUri(uri) && isDocumentsProvider(context, uri.getAuthority())) {
+            final List<String> paths = uri.getPathSegments();
+            if (paths.size() == 2) {
+                return PATH_DOCUMENT.equals(paths.get(0));
+            } else if (paths.size() == 4) {
+                return PATH_TREE.equals(paths.get(0)) && PATH_DOCUMENT.equals(paths.get(2));
+            }
         }
         return false;
     }
 
     /** {@hide} */
+    public static boolean isRootUri(Context context, @Nullable Uri uri) {
+        if (isContentUri(uri) && isDocumentsProvider(context, uri.getAuthority())) {
+            final List<String> paths = uri.getPathSegments();
+            return (paths.size() == 2 && PATH_ROOT.equals(paths.get(0)));
+        }
+        return false;
+    }
+
+    /** {@hide} */
+    public static boolean isContentUri(@Nullable Uri uri) {
+        return uri != null && ContentResolver.SCHEME_CONTENT.equals(uri.getScheme());
+    }
+
+    /** {@hide} */
     public static boolean isTreeUri(Uri uri) {
         final List<String> paths = uri.getPathSegments();
         return (paths.size() >= 2 && PATH_TREE.equals(paths.get(0)));
diff --git a/core/java/android/service/notification/ConditionProviderService.java b/core/java/android/service/notification/ConditionProviderService.java
index 2a8fb2c..c679eda 100644
--- a/core/java/android/service/notification/ConditionProviderService.java
+++ b/core/java/android/service/notification/ConditionProviderService.java
@@ -35,8 +35,7 @@
  * the {@link android.Manifest.permission#BIND_CONDITION_PROVIDER_SERVICE} permission
  * and include an intent filter with the {@link #SERVICE_INTERFACE} action. If you want users to be
  * able to create and update conditions for this service to monitor, include the
- * {@link #META_DATA_RULE_TYPE}, {@link #META_DATA_DEFAULT_CONDITION_ID}, and
- * {@link #META_DATA_CONFIGURATION_ACTIVITY} tags. For example:</p>
+ * {@link #META_DATA_RULE_TYPE} and {@link #META_DATA_CONFIGURATION_ACTIVITY} tags. For example:</p>
  * <pre>
  * &lt;service android:name=".MyConditionProvider"
  *          android:label="&#64;string/service_name"
@@ -49,10 +48,6 @@
  *               android:value="@string/my_condition_rule">
  *           &lt;/meta-data>
  *           &lt;meta-data
- *               android:name="android.service.zen.automatic.defaultConditionId"
- *               android:value="condition://com.my.package/mycondition">
- *           &lt;/meta-data>
- *           &lt;meta-data
  *               android:name="android.service.zen.automatic.configurationActivity"
  *               android:value="com.my.package/.MyConditionConfigurationActivity">
  *           &lt;/meta-data>
@@ -82,13 +77,6 @@
     public static final String META_DATA_RULE_TYPE = "android.service.zen.automatic.ruleType";
 
     /**
-     * The name of the {@code meta-data} tag containing a default Condition {@link Uri} that can
-     * be parsed by this service.
-     */
-    public static final String META_DATA_DEFAULT_CONDITION_ID =
-            "android.service.zen.automatic.defaultConditionId";
-
-    /**
      * The name of the {@code meta-data} tag containing the {@link ComponentName} of an activity
      * that allows users to configure the conditions provided by this service.
      */
@@ -96,17 +84,9 @@
             "android.service.zen.automatic.configurationActivity";
 
     /**
-     * A condition {@link Uri} extra passed to {@link #META_DATA_CONFIGURATION_ACTIVITY}. If the
-     * condition Uri is modified by that activity, it must be included in the result Intent extras
-     * in order to be persisted.
+     * A String rule id extra passed to {@link #META_DATA_CONFIGURATION_ACTIVITY}.
      */
-    public static final String EXTRA_CONDITION_ID = "android.content.automatic.conditionId";
-
-    /**
-     * A String rule name extra passed to {@link #META_DATA_CONFIGURATION_ACTIVITY}. This extra is
-     * informative only, and will be ignored if included in the result Intent extras.
-     */
-    public static final String EXTRA_RULE_NAME = "android.content.automatic.ruleName";
+    public static final String EXTRA_RULE_ID = "android.content.automatic.ruleId";
 
     /**
      * Called when this service is connected.
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index 4de903e..e054a61 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -110,6 +110,7 @@
     private static final String RULE_ATT_COMPONENT = "component";
     private static final String RULE_ATT_ZEN = "zen";
     private static final String RULE_ATT_CONDITION_ID = "conditionId";
+    private static final String RULE_ATT_CREATION_TIME = "creationTime";
 
     public boolean allowCalls = DEFAULT_ALLOW_CALLS;
     public boolean allowRepeatCallers = DEFAULT_ALLOW_REPEAT_CALLERS;
@@ -415,6 +416,7 @@
                     final String id = parser.getAttributeValue(null, RULE_ATT_ID);
                     final ZenRule automaticRule = readRuleXml(parser);
                     if (id != null && automaticRule != null) {
+                        automaticRule.id = id;
                         rt.automaticRules.put(id, automaticRule);
                     }
                 }
@@ -468,6 +470,7 @@
         }
         rt.conditionId = safeUri(parser, RULE_ATT_CONDITION_ID);
         rt.component = safeComponentName(parser, RULE_ATT_COMPONENT);
+        rt.creationTime = safeLong(parser, RULE_ATT_CREATION_TIME, 0);
         rt.condition = readConditionXml(parser);
         return rt;
     }
@@ -485,6 +488,7 @@
         if (rule.conditionId != null) {
             out.attribute(null, RULE_ATT_CONDITION_ID, rule.conditionId.toString());
         }
+        out.attribute(null, RULE_ATT_CREATION_TIME, Long.toString(rule.creationTime));
         if (rule.condition != null) {
             writeConditionXml(rule.condition, out);
         }
@@ -552,6 +556,11 @@
         return Uri.parse(val);
     }
 
+    private static long safeLong(XmlPullParser parser, String att, long defValue) {
+        final String val = parser.getAttributeValue(null, att);
+        return tryParseLong(val, defValue);
+    }
+
     public ArraySet<String> getAutomaticRuleNames() {
         final ArraySet<String> rt = new ArraySet<String>();
         for (int i = 0; i < automaticRules.size(); i++) {
@@ -887,7 +896,7 @@
         return Global.isValidZenMode(rt) ? rt : defValue;
     }
 
-    public String newRuleId() {
+    public static String newRuleId() {
         return UUID.randomUUID().toString().replace("-", "");
     }
 
@@ -943,6 +952,8 @@
         public Uri conditionId;          // required for automatic
         public Condition condition;      // optional
         public ComponentName component;  // optional
+        public String id;                // required for automatic (unique)
+        public long creationTime;        // required for automatic
 
         public ZenRule() { }
 
@@ -956,6 +967,10 @@
             conditionId = source.readParcelable(null);
             condition = source.readParcelable(null);
             component = source.readParcelable(null);
+            if (source.readInt() == 1) {
+                id = source.readString();
+            }
+            creationTime = source.readLong();
         }
 
         @Override
@@ -977,6 +992,13 @@
             dest.writeParcelable(conditionId, 0);
             dest.writeParcelable(condition, 0);
             dest.writeParcelable(component, 0);
+            if (id != null) {
+                dest.writeInt(1);
+                dest.writeString(id);
+            } else {
+                dest.writeInt(0);
+            }
+            dest.writeLong(creationTime);
         }
 
         @Override
@@ -989,6 +1011,8 @@
                     .append(",conditionId=").append(conditionId)
                     .append(",condition=").append(condition)
                     .append(",component=").append(component)
+                    .append(",id=").append(id)
+                    .append(",creationTime=").append(creationTime)
                     .append(']').toString();
         }
 
@@ -1029,6 +1053,12 @@
             if (!Objects.equals(component, to.component)) {
                 d.addLine(item, "component", component, to.component);
             }
+            if (!Objects.equals(id, to.id)) {
+                d.addLine(item, "id", id, to.id);
+            }
+            if (creationTime == to.creationTime) {
+                d.addLine(item, "creationTime", creationTime, to.creationTime);
+            }
         }
 
         @Override
@@ -1042,13 +1072,15 @@
                     && other.zenMode == zenMode
                     && Objects.equals(other.conditionId, conditionId)
                     && Objects.equals(other.condition, condition)
-                    && Objects.equals(other.component, component);
+                    && Objects.equals(other.component, component)
+                    && Objects.equals(other.id, id)
+                    && other.creationTime == creationTime;
         }
 
         @Override
         public int hashCode() {
             return Objects.hash(enabled, snoozing, name, zenMode, conditionId, condition,
-                    component);
+                    component, id, creationTime);
         }
 
         public boolean isAutomaticActive() {
diff --git a/core/java/android/util/MathUtils.java b/core/java/android/util/MathUtils.java
index 8b57d3d..32aac13 100644
--- a/core/java/android/util/MathUtils.java
+++ b/core/java/android/util/MathUtils.java
@@ -156,6 +156,27 @@
         return start + (stop - start) * amount;
     }
 
+    /**
+     * Returns an interpolated angle in degrees between a set of start and end
+     * angles.
+     * <p>
+     * Unlike {@link #lerp(float, float, float)}, the direction and distance of
+     * travel is determined by the shortest angle between the start and end
+     * angles. For example, if the starting angle is 0 and the ending angle is
+     * 350, then the interpolated angle will be in the range [0,-10] rather
+     * than [0,350].
+     *
+     * @param start the starting angle in degrees
+     * @param end the ending angle in degrees
+     * @param amount the position between start and end in the range [0,1]
+     *               where 0 is the starting angle and 1 is the ending angle
+     * @return the interpolated angle in degrees
+     */
+    public static float lerpDeg(float start, float end, float amount) {
+        final float minAngle = (((end - start) + 180) % 360) - 180;
+        return minAngle * amount + start;
+    }
+
     public static float norm(float start, float stop, float value) {
         return (value - start) / (stop - start);
     }
@@ -185,4 +206,24 @@
     public static void randomSeed(long seed) {
         sRandom.setSeed(seed);
     }
+
+    /**
+     * Returns the sum of the two parameters, or throws an exception if the resulting sum would
+     * cause an overflow or underflow.
+     * @throws IllegalArgumentException when overflow or underflow would occur.
+     */
+    public static int addOrThrow(int a, int b) throws IllegalArgumentException {
+        if (b == 0) {
+            return a;
+        }
+
+        if (b > 0 && a <= (Integer.MAX_VALUE - b)) {
+            return a + b;
+        }
+
+        if (b < 0 && a >= (Integer.MIN_VALUE - b)) {
+            return a + b;
+        }
+        throw new IllegalArgumentException("Addition overflow: " + a + " + " + b);
+    }
 }
diff --git a/core/java/android/view/IWindow.aidl b/core/java/android/view/IWindow.aidl
index acad496..cc4bcb6 100644
--- a/core/java/android/view/IWindow.aidl
+++ b/core/java/android/view/IWindow.aidl
@@ -80,20 +80,6 @@
             int localValue, int localChanges);
 
     /**
-     * The window is beginning to animate. The application should stop drawing frames until the
-     * window is not animating anymore, indicated by being called {@link #windowEndAnimating}.
-     *
-     * @param remainingFrameCount how many frames the app might still draw before stopping drawing;
-     *                            pass -1 to let it continue drawing
-     */
-    void onAnimationStarted(int remainingFrameCount);
-
-    /**
-     * The window has ended animating. See {@link #onAnimationStarted}.
-     */
-    void onAnimationStopped();
-
-    /**
      * Called for non-application windows when the enter animation has completed.
      */
     void dispatchWindowShown();
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 7d48a9a..db68c29 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -447,10 +447,11 @@
         final boolean formatChanged = mFormat != mRequestedFormat;
         final boolean sizeChanged = mWidth != myWidth || mHeight != myHeight;
         final boolean visibleChanged = mVisible != mRequestedVisible;
+        final boolean layoutSizeChanged = getWidth() != mLayout.width || getHeight() != mLayout.height;
 
         if (force || creating || formatChanged || sizeChanged || visibleChanged
             || mLeft != mLocation[0] || mTop != mLocation[1]
-            || mUpdateWindowNeeded || mReportDrawNeeded || redrawNeeded) {
+            || mUpdateWindowNeeded || mReportDrawNeeded || redrawNeeded || layoutSizeChanged) {
 
             if (DEBUG) Log.i(TAG, "Changes: creating=" + creating
                     + " format=" + formatChanged + " size=" + sizeChanged
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index dcef142..304e9c0 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -349,7 +349,7 @@
      * @param right The right side of the protected bounds.
      * @param bottom The bottom side of the protected bounds.
      */
-    public void setContentOverdrawProtectionBounds(int left, int top, int right, int bottom) {
+    public void setContentDrawBounds(int left, int top, int right, int bottom) {
         mStagedContentBounds.set(left, top, right, bottom);
     }
 
@@ -370,9 +370,9 @@
         // renderer.
         if (!mCurrentContentBounds.equals(mStagedContentBounds)) {
             mCurrentContentBounds.set(mStagedContentBounds);
-            nSetContentOverdrawProtectionBounds(mNativeProxy, mCurrentContentBounds.left,
-                mCurrentContentBounds.top, mCurrentContentBounds.right,
-            mCurrentContentBounds.bottom);
+            nSetContentDrawBounds(mNativeProxy, mCurrentContentBounds.left,
+                    mCurrentContentBounds.top, mCurrentContentBounds.right,
+                    mCurrentContentBounds.bottom);
         }
 
         attachInfo.mIgnoreDirtyState = false;
@@ -600,6 +600,6 @@
              boolean placeFront);
     private static native void nRemoveRenderNode(long nativeProxy, long rootRenderNode);
     private static native void nDrawRenderNode(long nativeProxy, long rootRenderNode);
-    private static native void nSetContentOverdrawProtectionBounds(long nativeProxy, int left,
+    private static native void nSetContentDrawBounds(long nativeProxy, int left,
              int top, int right, int bottom);
 }
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index f6c60ed..14e7d6c 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -73,6 +73,7 @@
 import android.view.animation.Interpolator;
 import android.view.inputmethod.InputConnection;
 import android.view.inputmethod.InputMethodManager;
+import android.view.WindowCallbacks;
 import android.widget.Scroller;
 
 import com.android.internal.R;
@@ -115,6 +116,12 @@
     private static final boolean DEBUG_INPUT_STAGES = false || LOCAL_LOGV;
 
     /**
+     * Set to false if we do not want to use the multi threaded renderer. Note that by disabling
+     * this, WindowCallbacks will not fire.
+     */
+    private static final boolean USE_MT_RENDERER = true;
+
+    /**
      * Set this system property to true to force the view hierarchy to render
      * at 60 Hz. This can be used to measure the potential framerate.
      */
@@ -132,12 +139,12 @@
 
     static final ThreadLocal<HandlerActionQueue> sRunQueues = new ThreadLocal<HandlerActionQueue>();
 
-    static final ArrayList<Runnable> sFirstDrawHandlers = new ArrayList<Runnable>();
+    static final ArrayList<Runnable> sFirstDrawHandlers = new ArrayList();
     static boolean sFirstDrawComplete = false;
 
-    static final ArrayList<ComponentCallbacks> sConfigCallbacks
-            = new ArrayList<ComponentCallbacks>();
+    static final ArrayList<ComponentCallbacks> sConfigCallbacks = new ArrayList();
 
+    final ArrayList<WindowCallbacks> mWindowCallbacks = new ArrayList();
     final Context mContext;
     final IWindowSession mWindowSession;
     final Display mDisplay;
@@ -231,11 +238,7 @@
     boolean mNewSurfaceNeeded;
     boolean mHasHadWindowFocus;
     boolean mLastWasImTarget;
-    boolean mWindowsAnimating;
-    boolean mDrawDuringWindowsAnimating;
 
-    /** How many frames the app is still allowed to draw when a window animation is happening. */
-    private int mRemainingFrameCount;
     boolean mIsDrawing;
     int mLastSystemUiVisibility;
     int mClientWindowLayoutFlags;
@@ -417,6 +420,22 @@
         }
     }
 
+    public void addWindowCallbacks(WindowCallbacks callback) {
+        if (USE_MT_RENDERER) {
+            synchronized (mWindowCallbacks) {
+                mWindowCallbacks.add(callback);
+            }
+        }
+    }
+
+    public void removeWindowCallbacks(WindowCallbacks callback) {
+        if (USE_MT_RENDERER) {
+            synchronized (mWindowCallbacks) {
+                mWindowCallbacks.remove(callback);
+            }
+        }
+    }
+
     // FIXME for perf testing only
     private boolean mProfile = false;
 
@@ -1378,6 +1397,7 @@
             mAttachInfo.mWindowVisibility = viewVisibility;
             host.dispatchWindowVisibilityChanged(viewVisibility);
             if (viewVisibility != View.VISIBLE || mNewSurfaceNeeded) {
+                endDragResizing();
                 destroyHardwareResources();
             }
             if (viewVisibility == View.GONE) {
@@ -1701,14 +1721,20 @@
                 final boolean dragResizing = (relayoutResult
                         & WindowManagerGlobal.RELAYOUT_RES_DRAG_RESIZING) != 0;
                 if (mDragResizing != dragResizing) {
-                    mDragResizing = dragResizing;
-                    mFullRedrawNeeded = true;
+                    if (dragResizing) {
+                        startDragResizing(frame);
+                    } else {
+                        // We shouldn't come here, but if we come we should end the resize.
+                        endDragResizing();
+                    }
                 }
-                if (dragResizing) {
-                    mCanvasOffsetX = mWinFrame.left;
-                    mCanvasOffsetY = mWinFrame.top;
-                } else {
-                    mCanvasOffsetX = mCanvasOffsetY = 0;
+                if (!USE_MT_RENDERER) {
+                    if (dragResizing) {
+                        mCanvasOffsetX = mWinFrame.left;
+                        mCanvasOffsetY = mWinFrame.top;
+                    } else {
+                        mCanvasOffsetX = mCanvasOffsetY = 0;
+                    }
                 }
             } catch (RemoteException e) {
             }
@@ -1948,8 +1974,6 @@
             }
         }
 
-        boolean skipDraw = false;
-
         if (mFirst) {
             // handle first focus request
             if (DEBUG_INPUT_RESIZE) Log.v(TAG, "First: mView.hasFocus()="
@@ -1964,11 +1988,6 @@
                             + mView.findFocus());
                 }
             }
-        } else if (mWindowsAnimating) {
-            if (mRemainingFrameCount <= 0) {
-                skipDraw = true;
-            }
-            mRemainingFrameCount--;
         }
 
         final boolean changedVisibility = (viewVisibilityChanged || mFirst) && isViewVisible;
@@ -2013,16 +2032,14 @@
         boolean cancelDraw = mAttachInfo.mTreeObserver.dispatchOnPreDraw() || !isViewVisible;
 
         if (!cancelDraw && !newSurface) {
-            if (!skipDraw || mReportNextDraw) {
-                if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
-                    for (int i = 0; i < mPendingTransitions.size(); ++i) {
-                        mPendingTransitions.get(i).startChangingAnimations();
-                    }
-                    mPendingTransitions.clear();
+            if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
+                for (int i = 0; i < mPendingTransitions.size(); ++i) {
+                    mPendingTransitions.get(i).startChangingAnimations();
                 }
-
-                performDraw();
+                mPendingTransitions.clear();
             }
+
+            performDraw();
         } else {
             if (isViewVisible) {
                 // Try again
@@ -2549,6 +2566,14 @@
 
                 dirty.setEmpty();
 
+                // Stage the content drawn size now. It will be transferred to the renderer
+                // shortly before the draw commands get send to the renderer.
+                synchronized (mWindowCallbacks) {
+                    for (int i = mWindowCallbacks.size() - 1; i >= 0; i--) {
+                        mWindowCallbacks.get(i).onContentDraw(mWindowAttributes.surfaceInsets.left,
+                                mWindowAttributes.surfaceInsets.top, mWidth, mHeight);
+                    }
+                }
                 mAttachInfo.mHardwareRenderer.draw(mView, mAttachInfo, this);
             } else {
                 // If we get here with a disabled & requested hardware renderer, something went
@@ -2757,16 +2782,6 @@
         return mAttachInfo.mAccessibilityFocusDrawable;
     }
 
-    /**
-     * @hide
-     */
-    public void setDrawDuringWindowsAnimating(boolean value) {
-        mDrawDuringWindowsAnimating = value;
-        if (value) {
-            handleDispatchWindowAnimationStopped();
-        }
-    }
-
     boolean scrollToRectOrFocus(Rect rectangle, boolean immediate) {
         final Rect ci = mAttachInfo.mContentInsets;
         final Rect vi = mAttachInfo.mVisibleInsets;
@@ -3130,8 +3145,6 @@
     private final static int MSG_WINDOW_MOVED = 23;
     private final static int MSG_SYNTHESIZE_INPUT_EVENT = 24;
     private final static int MSG_DISPATCH_WINDOW_SHOWN = 25;
-    private final static int MSG_DISPATCH_WINDOW_ANIMATION_STOPPED = 26;
-    private final static int MSG_DISPATCH_WINDOW_ANIMATION_STARTED = 27;
 
     final class ViewRootHandler extends Handler {
         @Override
@@ -3175,10 +3188,6 @@
                     return "MSG_PROCESS_INPUT_EVENTS";
                 case MSG_CLEAR_ACCESSIBILITY_FOCUS_HOST:
                     return "MSG_CLEAR_ACCESSIBILITY_FOCUS_HOST";
-                case MSG_DISPATCH_WINDOW_ANIMATION_STARTED:
-                    return "MSG_DISPATCH_WINDOW_ANIMATION_STARTED";
-                case MSG_DISPATCH_WINDOW_ANIMATION_STOPPED:
-                    return "MSG_DISPATCH_WINDOW_ANIMATION_STOPPED";
                 case MSG_WINDOW_MOVED:
                     return "MSG_WINDOW_MOVED";
                 case MSG_SYNTHESIZE_INPUT_EVENT:
@@ -3399,13 +3408,6 @@
             case MSG_CLEAR_ACCESSIBILITY_FOCUS_HOST: {
                 setAccessibilityFocus(null, null);
             } break;
-            case MSG_DISPATCH_WINDOW_ANIMATION_STARTED: {
-                int remainingFrameCount = msg.arg1;
-                handleDispatchWindowAnimationStarted(remainingFrameCount);
-            } break;
-            case MSG_DISPATCH_WINDOW_ANIMATION_STOPPED: {
-                handleDispatchWindowAnimationStopped();
-            } break;
             case MSG_INVALIDATE_WORLD: {
                 if (mView != null) {
                     invalidateWorld(mView);
@@ -4018,9 +4020,6 @@
             if (q.mEvent instanceof KeyEvent) {
                 return processKeyEvent(q);
             } else {
-                // If delivering a new non-key event, make sure the window is
-                // now allowed to start updating.
-                handleDispatchWindowAnimationStopped();
                 final int source = q.mEvent.getSource();
                 if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
                     return processPointerEvent(q);
@@ -4047,12 +4046,6 @@
         private int processKeyEvent(QueuedInputEvent q) {
             final KeyEvent event = (KeyEvent)q.mEvent;
 
-            if (event.getAction() != KeyEvent.ACTION_UP) {
-                // If delivering a new key event, make sure the window is
-                // now allowed to start updating.
-                handleDispatchWindowAnimationStopped();
-            }
-
             // Deliver the key to the view hierarchy.
             if (mView.dispatchKeyEvent(event)) {
                 return FINISH_HANDLED;
@@ -5267,22 +5260,6 @@
         }
     }
 
-    public void handleDispatchWindowAnimationStarted(int remainingFrameCount) {
-        if (!mDrawDuringWindowsAnimating && remainingFrameCount != -1) {
-            mRemainingFrameCount = remainingFrameCount;
-            mWindowsAnimating = true;
-        }
-    }
-
-    public void handleDispatchWindowAnimationStopped() {
-        if (mWindowsAnimating) {
-            mWindowsAnimating = false;
-            if (!mDirty.isEmpty() || mIsAnimating || mFullRedrawNeeded)  {
-                scheduleTraversals();
-            }
-        }
-    }
-
     public void handleDispatchWindowShown() {
         mAttachInfo.mTreeObserver.dispatchOnWindowShown();
     }
@@ -5623,6 +5600,17 @@
                 + " contentInsets=" + contentInsets.toShortString()
                 + " visibleInsets=" + visibleInsets.toShortString()
                 + " reportDraw=" + reportDraw);
+
+        // Tell all listeners that we are resizing the window so that the chrome can get
+        // updated as fast as possible on a separate thread,
+        if (mDragResizing) {
+            synchronized (mWindowCallbacks) {
+                for (int i = mWindowCallbacks.size() - 1; i >= 0; i--) {
+                    mWindowCallbacks.get(i).onWindowSizeIsChanging(frame);
+                }
+            }
+        }
+
         Message msg = mHandler.obtainMessage(reportDraw ? MSG_RESIZED_REPORT : MSG_RESIZED);
         if (mTranslator != null) {
             mTranslator.translateRectInScreenToAppWindow(frame);
@@ -6194,15 +6182,6 @@
         mHandler.sendMessage(mHandler.obtainMessage(MSG_DISPATCH_SYSTEM_UI_VISIBILITY, args));
     }
 
-    public void dispatchWindowAnimationStarted(int remainingFrameCount) {
-        mHandler.obtainMessage(MSG_DISPATCH_WINDOW_ANIMATION_STARTED,
-                remainingFrameCount, 0 /* unused */).sendToTarget();
-    }
-
-    public void dispatchWindowAnimationStopped() {
-        mHandler.sendEmptyMessage(MSG_DISPATCH_WINDOW_ANIMATION_STOPPED);
-    }
-
     public void dispatchCheckFocus() {
         if (!mHandler.hasMessages(MSG_CHECK_FOCUS)) {
             // This will result in a call to checkFocus() below.
@@ -6763,22 +6742,6 @@
         }
 
         @Override
-        public void onAnimationStarted(int remainingFrameCount) {
-            final ViewRootImpl viewAncestor = mViewAncestor.get();
-            if (viewAncestor != null) {
-                viewAncestor.dispatchWindowAnimationStarted(remainingFrameCount);
-            }
-        }
-
-        @Override
-        public void onAnimationStopped() {
-            final ViewRootImpl viewAncestor = mViewAncestor.get();
-            if (viewAncestor != null) {
-                viewAncestor.dispatchWindowAnimationStopped();
-            }
-        }
-
-        @Override
         public void dispatchWindowShown() {
             final ViewRootImpl viewAncestor = mViewAncestor.get();
             if (viewAncestor != null) {
@@ -6804,6 +6767,36 @@
     }
 
     /**
+     * Start a drag resizing which will inform all listeners that a window resize is taking place.
+     */
+    private void startDragResizing(Rect initialBounds) {
+        if (!mDragResizing) {
+            mDragResizing = true;
+            synchronized (mWindowCallbacks) {
+                for (int i = mWindowCallbacks.size() - 1; i >= 0; i--) {
+                    mWindowCallbacks.get(i).onWindowDragResizeStart(initialBounds);
+                }
+            }
+            mFullRedrawNeeded = true;
+        }
+    }
+
+    /**
+     * End a drag resize which will inform all listeners that a window resize has ended.
+     */
+    private void endDragResizing() {
+        if (mDragResizing) {
+            mDragResizing = false;
+            synchronized (mWindowCallbacks) {
+                for (int i = mWindowCallbacks.size() - 1; i >= 0; i--) {
+                    mWindowCallbacks.get(i).onWindowDragResizeEnd();
+                }
+            }
+            mFullRedrawNeeded = true;
+        }
+    }
+
+    /**
      * Class for managing the accessibility interaction connection
      * based on the global accessibility state.
      */
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index 5f4e7af..13544851 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -28,9 +28,8 @@
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
-import android.graphics.drawable.Drawable;
 import android.graphics.PixelFormat;
-import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
 import android.media.session.MediaController;
 import android.net.Uri;
 import android.os.Bundle;
@@ -2007,16 +2006,6 @@
      */
     public abstract void setNavigationBarColor(@ColorInt int color);
 
-    /**
-     * Get information whether the activity has non client decoration view. These views are used in
-     * the multi window environment, to provide dragging handle and maximize/close buttons.
-     *
-     * @hide
-     */
-    public boolean hasNonClientDecorView() {
-        return false;
-    }
-
     /** @hide */
     public void setTheme(int resId) {
     }
diff --git a/core/java/android/view/WindowCallbacks.java b/core/java/android/view/WindowCallbacks.java
new file mode 100644
index 0000000..1aeaec8
--- /dev/null
+++ b/core/java/android/view/WindowCallbacks.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.view;
+
+import android.graphics.Rect;
+
+/**
+ * These callbacks are used to communicate window configuration changes while the user is performing
+ * window changes.
+ * Note: Note that at the time of onWindowDragResizeStart the content size isn't known. A consumer
+ * should therfore not draw anything before the additional onContentDraw call has arrived.
+ * @hide
+ */
+public interface WindowCallbacks {
+    /**
+     * Called by the system when the window got changed by the user, before the layouter got called.
+     * It can be used to perform a "quick and dirty" resize which should never take more then 4ms to
+     * complete.
+     *
+     * <p>At the time the layouting has not happened yet.
+     *
+     * @param newBounds The new window frame bounds.
+     */
+    void onWindowSizeIsChanging(Rect newBounds);
+
+    /**
+     * Called when a drag resize starts.
+     * @param initialBounds The initial bounds where the window will be.
+     */
+    void onWindowDragResizeStart(Rect initialBounds);
+
+    /**
+     * Called when a drag resize ends.
+     */
+    void onWindowDragResizeEnd();
+
+    /**
+     * The content will now be drawn to these bounds.
+     */
+    void onContentDraw(int offsetX, int offsetY, int sizeX, int sizeY);
+}
diff --git a/core/java/android/view/WindowManagerInternal.java b/core/java/android/view/WindowManagerInternal.java
index a6d9932..3882bca 100644
--- a/core/java/android/view/WindowManagerInternal.java
+++ b/core/java/android/view/WindowManagerInternal.java
@@ -263,4 +263,7 @@
       */
     public abstract void setOnHardKeyboardStatusChangeListener(
         OnHardKeyboardStatusChangeListener listener);
+
+    /** Returns true if the stack with the input Id is currently visible. */
+    public abstract boolean isStackVisible(int stackId);
 }
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index 72971e8..81ec330 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -88,6 +88,9 @@
     /** @hide */
     public static final int DALTONIZER_CORRECT_DEUTERANOMALY = 12;
 
+    /** @hide */
+    public static final int AUTOCLICK_DELAY_DEFAULT = 600;
+
     static final Object sInstanceSync = new Object();
 
     private static AccessibilityManager sInstance;
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index d0c50c9..358f939 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -20,6 +20,7 @@
 import android.annotation.SystemApi;
 import android.annotation.Widget;
 import android.content.Context;
+import android.content.Intent;
 import android.content.res.Configuration;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
@@ -1850,7 +1851,7 @@
      * <a href="https://html.spec.whatwg.org/multipage/comms.html#messagechannel">here
      * </a>
      *
-     * The returned message channels are entangled and already in started state.
+     * <p>The returned message channels are entangled and already in started state.</p>
      *
      * @return the two message ports that form the message channel.
      */
@@ -2157,6 +2158,10 @@
             WebView.super.setLayoutParams(params);
         }
 
+        public void super_startActivityForResult(Intent intent, int requestCode) {
+            WebView.super.startActivityForResult(intent, requestCode);
+        }
+
         // ---- Access to non-public methods ----
         public void overScrollBy(int deltaX, int deltaY,
                 int scrollX, int scrollY,
@@ -2594,6 +2599,23 @@
         mProvider.getViewDelegate().onFinishTemporaryDetach();
     }
 
+    /**
+     * Receive the result from a previous call to {@link #startActivityForResult(Intent, int)}.
+     *
+     * @param requestCode The integer request code originally supplied to
+     *                    startActivityForResult(), allowing you to identify who this
+     *                    result came from.
+     * @param resultCode The integer result code returned by the child activity
+     *                   through its setResult().
+     * @param data An Intent, which can return result data to the caller
+     *               (various data can be attached to Intent "extras").
+     * @hide
+     */
+    @Override
+    public void onActivityResult(int requestCode, int resultCode, Intent data) {
+        mProvider.getViewDelegate().onActivityResult(requestCode, resultCode, data);
+    }
+
     /** @hide */
     @Override
     protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) {
diff --git a/core/java/android/webkit/WebViewProvider.java b/core/java/android/webkit/WebViewProvider.java
index 27033ad..3ce034c 100644
--- a/core/java/android/webkit/WebViewProvider.java
+++ b/core/java/android/webkit/WebViewProvider.java
@@ -18,6 +18,7 @@
 
 import android.annotation.SystemApi;
 import android.content.res.Configuration;
+import android.content.Intent;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Paint;
@@ -380,6 +381,8 @@
         public void onStartTemporaryDetach();
 
         public void onFinishTemporaryDetach();
+
+        public void onActivityResult(int requestCode, int resultCode, Intent data);
     }
 
     interface ScrollDelegate {
diff --git a/core/java/android/widget/AdapterViewAnimator.java b/core/java/android/widget/AdapterViewAnimator.java
index 932b354..0e3a69f 100644
--- a/core/java/android/widget/AdapterViewAnimator.java
+++ b/core/java/android/widget/AdapterViewAnimator.java
@@ -401,12 +401,11 @@
      }
 
     LayoutParams createOrReuseLayoutParams(View v) {
-        final ViewGroup.LayoutParams currentLp = v.getLayoutParams();
-        if (currentLp instanceof ViewGroup.LayoutParams) {
-            LayoutParams lp = (LayoutParams) currentLp;
-            return lp;
+        final LayoutParams currentLp = v.getLayoutParams();
+        if (currentLp != null) {
+            return currentLp;
         }
-        return new ViewGroup.LayoutParams(0, 0);
+        return new LayoutParams(0, 0);
     }
 
     void refreshChildren() {
diff --git a/core/java/android/widget/FastScroller.java b/core/java/android/widget/FastScroller.java
index c40289e..559181b 100644
--- a/core/java/android/widget/FastScroller.java
+++ b/core/java/android/widget/FastScroller.java
@@ -383,6 +383,7 @@
                     break;
             }
         }
+        ta.recycle();
 
         updateAppearance();
     }
diff --git a/core/java/android/widget/RadialTimePickerView.java b/core/java/android/widget/RadialTimePickerView.java
index 9571109..7220256 100644
--- a/core/java/android/widget/RadialTimePickerView.java
+++ b/core/java/android/widget/RadialTimePickerView.java
@@ -16,12 +16,7 @@
 
 package android.widget;
 
-import android.animation.Animator;
-import android.animation.AnimatorSet;
-import android.animation.Keyframe;
 import android.animation.ObjectAnimator;
-import android.animation.PropertyValuesHolder;
-import android.animation.ValueAnimator;
 import android.content.Context;
 import android.content.res.ColorStateList;
 import android.content.res.Resources;
@@ -35,6 +30,7 @@
 import android.graphics.Typeface;
 import android.os.Bundle;
 import android.util.AttributeSet;
+import android.util.FloatProperty;
 import android.util.IntArray;
 import android.util.Log;
 import android.util.MathUtils;
@@ -50,7 +46,6 @@
 import com.android.internal.R;
 import com.android.internal.widget.ExploreByTouchHelper;
 
-import java.util.ArrayList;
 import java.util.Calendar;
 import java.util.Locale;
 
@@ -60,6 +55,7 @@
  * @hide
  */
 public class RadialTimePickerView extends View {
+
     private static final String TAG = "RadialTimePickerView";
 
     private static final int HOURS = 0;
@@ -73,12 +69,6 @@
     private static final int AM = 0;
     private static final int PM = 1;
 
-    // Opaque alpha level
-    private static final int ALPHA_OPAQUE = 255;
-
-    // Transparent alpha level
-    private static final int ALPHA_TRANSPARENT = 0;
-
     private static final int HOURS_IN_CIRCLE = 12;
     private static final int MINUTES_IN_CIRCLE = 60;
     private static final int DEGREES_FOR_ONE_HOUR = 360 / HOURS_IN_CIRCLE;
@@ -88,8 +78,8 @@
     private static final int[] HOURS_NUMBERS_24 = {0, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23};
     private static final int[] MINUTES_NUMBERS = {0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55};
 
-    private static final int FADE_OUT_DURATION = 500;
-    private static final int FADE_IN_DURATION = 500;
+    private static final int ANIM_DURATION_NORMAL = 500;
+    private static final int ANIM_DURATION_TOUCH = 60;
 
     private static final int[] SNAP_PREFER_30S_MAP = new int[361];
 
@@ -97,6 +87,9 @@
     private static final float[] COS_30 = new float[NUM_POSITIONS];
     private static final float[] SIN_30 = new float[NUM_POSITIONS];
 
+    /** "Something is wrong" color used when a color attribute is missing. */
+    private static final int MISSING_COLOR = Color.MAGENTA;
+
     static {
         // Prepare mapping to snap touchable degrees to selectable degrees.
         preparePrefer30sMap();
@@ -110,8 +103,19 @@
         }
     }
 
-    private final InvalidateUpdateListener mInvalidateUpdateListener =
-            new InvalidateUpdateListener();
+    private final FloatProperty<RadialTimePickerView> HOURS_TO_MINUTES =
+            new FloatProperty<RadialTimePickerView>("hoursToMinutes") {
+                @Override
+                public Float get(RadialTimePickerView radialTimePickerView) {
+                    return radialTimePickerView.mHoursToMinutes;
+                }
+
+                @Override
+                public void setValue(RadialTimePickerView object, float value) {
+                    object.mHoursToMinutes = value;
+                    object.invalidate();
+                }
+            };
 
     private final String[] mHours12Texts = new String[12];
     private final String[] mOuterHours24Texts = new String[12];
@@ -119,15 +123,8 @@
     private final String[] mMinutesTexts = new String[12];
 
     private final Paint[] mPaint = new Paint[2];
-    private final IntHolder[] mAlpha = new IntHolder[2];
-
     private final Paint mPaintCenter = new Paint();
-
-    private final Paint[][] mPaintSelector = new Paint[2][3];
-
-    private final int mSelectorColor;
-    private final int mSelectorDotColor;
-
+    private final Paint[] mPaintSelector = new Paint[3];
     private final Paint mPaintBackground = new Paint();
 
     private final Typeface mTypeface;
@@ -144,9 +141,6 @@
 
     private final int[] mSelectionDegrees = new int[2];
 
-    private final ArrayList<Animator> mHoursToMinutesAnims = new ArrayList<>();
-    private final ArrayList<Animator> mMinuteToHoursAnims = new ArrayList<>();
-
     private final RadialPickerTouchHelper mTouchHelper;
 
     private final Path mSelectorPath = new Path();
@@ -154,6 +148,9 @@
     private boolean mIs24HourMode;
     private boolean mShowHours;
 
+    private ObjectAnimator mHoursToMinutesAnimator;
+    private float mHoursToMinutes;
+
     /**
      * When in 24-hour mode, indicates that the current hour is between
      * 1 and 12 (inclusive).
@@ -165,6 +162,9 @@
     private int mSelectorDotRadius;
     private int mCenterDotRadius;
 
+    private int mSelectorColor;
+    private int mSelectorDotColor;
+
     private int mXCenter;
     private int mYCenter;
     private int mCircleRadius;
@@ -176,7 +176,6 @@
     private String[] mOuterTextHours;
     private String[] mInnerTextHours;
     private String[] mMinutesText;
-    private AnimatorSet mTransition;
 
     private int mAmOrPm;
 
@@ -304,27 +303,15 @@
             Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)  {
         super(context, attrs);
 
+        applyAttributes(attrs, defStyleAttr, defStyleRes);
+
         // Pull disabled alpha from theme.
         final TypedValue outValue = new TypedValue();
         context.getTheme().resolveAttribute(android.R.attr.disabledAlpha, outValue, true);
         mDisabledAlpha = outValue.getFloat();
 
-        // process style attributes
-        final Resources res = getResources();
-        final TypedArray a = mContext.obtainStyledAttributes(attrs, R.styleable.TimePicker,
-                defStyleAttr, defStyleRes);
-
         mTypeface = Typeface.create("sans-serif", Typeface.NORMAL);
 
-        // Initialize all alpha values to opaque.
-        for (int i = 0; i < mAlpha.length; i++) {
-            mAlpha[i] = new IntHolder(ALPHA_OPAQUE);
-        }
-
-        mTextColor[HOURS] = a.getColorStateList(R.styleable.TimePicker_numbersTextColor);
-        mTextColor[HOURS_INNER] = a.getColorStateList(R.styleable.TimePicker_numbersInnerTextColor);
-        mTextColor[MINUTES] = mTextColor[HOURS];
-
         mPaint[HOURS] = new Paint();
         mPaint[HOURS].setAntiAlias(true);
         mPaint[HOURS].setTextAlign(Paint.Align.CENTER);
@@ -333,44 +320,21 @@
         mPaint[MINUTES].setAntiAlias(true);
         mPaint[MINUTES].setTextAlign(Paint.Align.CENTER);
 
-        final ColorStateList selectorColors = a.getColorStateList(
-                R.styleable.TimePicker_numbersSelectorColor);
-        final int selectorActivatedColor = selectorColors.getColorForState(
-                StateSet.get(StateSet.VIEW_STATE_ENABLED | StateSet.VIEW_STATE_ACTIVATED), 0);
-
-        mPaintCenter.setColor(selectorActivatedColor);
         mPaintCenter.setAntiAlias(true);
 
-        final int[] activatedStateSet = StateSet.get(
-                StateSet.VIEW_STATE_ENABLED | StateSet.VIEW_STATE_ACTIVATED);
+        mPaintSelector[SELECTOR_CIRCLE] = new Paint();
+        mPaintSelector[SELECTOR_CIRCLE].setAntiAlias(true);
 
-        mSelectorColor = selectorActivatedColor;
-        mSelectorDotColor = mTextColor[HOURS].getColorForState(activatedStateSet, 0);
+        mPaintSelector[SELECTOR_DOT] = new Paint();
+        mPaintSelector[SELECTOR_DOT].setAntiAlias(true);
 
-        mPaintSelector[HOURS][SELECTOR_CIRCLE] = new Paint();
-        mPaintSelector[HOURS][SELECTOR_CIRCLE].setAntiAlias(true);
+        mPaintSelector[SELECTOR_LINE] = new Paint();
+        mPaintSelector[SELECTOR_LINE].setAntiAlias(true);
+        mPaintSelector[SELECTOR_LINE].setStrokeWidth(2);
 
-        mPaintSelector[HOURS][SELECTOR_DOT] = new Paint();
-        mPaintSelector[HOURS][SELECTOR_DOT].setAntiAlias(true);
-
-        mPaintSelector[HOURS][SELECTOR_LINE] = new Paint();
-        mPaintSelector[HOURS][SELECTOR_LINE].setAntiAlias(true);
-        mPaintSelector[HOURS][SELECTOR_LINE].setStrokeWidth(2);
-
-        mPaintSelector[MINUTES][SELECTOR_CIRCLE] = new Paint();
-        mPaintSelector[MINUTES][SELECTOR_CIRCLE].setAntiAlias(true);
-
-        mPaintSelector[MINUTES][SELECTOR_DOT] = new Paint();
-        mPaintSelector[MINUTES][SELECTOR_DOT].setAntiAlias(true);
-
-        mPaintSelector[MINUTES][SELECTOR_LINE] = new Paint();
-        mPaintSelector[MINUTES][SELECTOR_LINE].setAntiAlias(true);
-        mPaintSelector[MINUTES][SELECTOR_LINE].setStrokeWidth(2);
-
-        mPaintBackground.setColor(a.getColor(R.styleable.TimePicker_numbersBackgroundColor,
-                context.getColor(R.color.timepicker_default_numbers_background_color_material)));
         mPaintBackground.setAntiAlias(true);
 
+        final Resources res = getResources();
         mSelectorRadius = res.getDimensionPixelSize(R.dimen.timepicker_selector_radius);
         mSelectorStroke = res.getDimensionPixelSize(R.dimen.timepicker_selector_stroke);
         mSelectorDotRadius = res.getDimensionPixelSize(R.dimen.timepicker_selector_dot_radius);
@@ -385,6 +349,7 @@
         mTextInset[HOURS_INNER] = res.getDimensionPixelSize(R.dimen.timepicker_text_inset_inner);
 
         mShowHours = true;
+        mHoursToMinutes = HOURS;
         mIs24HourMode = false;
         mAmOrPm = AM;
 
@@ -399,8 +364,6 @@
         initHoursAndMinutesText();
         initData();
 
-        a.recycle();
-
         // Initial values
         final Calendar calendar = Calendar.getInstance(Locale.getDefault());
         final int currentHour = calendar.get(Calendar.HOUR_OF_DAY);
@@ -412,6 +375,48 @@
         setHapticFeedbackEnabled(true);
     }
 
+    void applyAttributes(AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+        final Context context = getContext();
+        final TypedArray a = getContext().obtainStyledAttributes(attrs,
+                R.styleable.TimePicker, defStyleAttr, defStyleRes);
+
+        final ColorStateList numbersTextColor = a.getColorStateList(
+                R.styleable.TimePicker_numbersTextColor);
+        final ColorStateList numbersInnerTextColor = a.getColorStateList(
+                R.styleable.TimePicker_numbersInnerTextColor);
+        mTextColor[HOURS] = numbersTextColor == null ?
+                ColorStateList.valueOf(MISSING_COLOR) : numbersTextColor;
+        mTextColor[HOURS_INNER] = numbersInnerTextColor == null ?
+                ColorStateList.valueOf(MISSING_COLOR) : numbersInnerTextColor;
+        mTextColor[MINUTES] = mTextColor[HOURS];
+
+        // Set up various colors derived from the selector "activated" state.
+        final ColorStateList selectorColors = a.getColorStateList(
+                R.styleable.TimePicker_numbersSelectorColor);
+        final int selectorActivatedColor;
+        if (selectorColors != null) {
+            final int[] stateSetEnabledActivated = StateSet.get(
+                    StateSet.VIEW_STATE_ENABLED | StateSet.VIEW_STATE_ACTIVATED);
+            selectorActivatedColor = selectorColors.getColorForState(
+                    stateSetEnabledActivated, 0);
+        }  else {
+            selectorActivatedColor = MISSING_COLOR;
+        }
+
+        mPaintCenter.setColor(selectorActivatedColor);
+
+        final int[] stateSetActivated = StateSet.get(
+                StateSet.VIEW_STATE_ENABLED | StateSet.VIEW_STATE_ACTIVATED);
+
+        mSelectorColor = selectorActivatedColor;
+        mSelectorDotColor = mTextColor[HOURS].getColorForState(stateSetActivated, 0);
+
+        mPaintBackground.setColor(a.getColor(R.styleable.TimePicker_numbersBackgroundColor,
+                context.getColor(R.color.timepicker_default_numbers_background_color_material)));
+
+        a.recycle();
+    }
+
     public void initialize(int hour, int minute, boolean is24HourMode) {
         if (mIs24HourMode != is24HourMode) {
             mIs24HourMode = is24HourMode;
@@ -569,35 +574,11 @@
     }
 
     public void showHours(boolean animate) {
-        if (mShowHours) {
-            return;
-        }
-
-        mShowHours = true;
-
-        if (animate) {
-            startMinutesToHoursAnimation();
-        }
-
-        initData();
-        invalidate();
-        mTouchHelper.invalidateRoot();
+        showPicker(true, animate);
     }
 
     public void showMinutes(boolean animate) {
-        if (!mShowHours) {
-            return;
-        }
-
-        mShowHours = false;
-
-        if (animate) {
-            startHoursToMinutesAnimation();
-        }
-
-        initData();
-        invalidate();
-        mTouchHelper.invalidateRoot();
+        showPicker(false, animate);
     }
 
     private void initHoursAndMinutesText() {
@@ -620,12 +601,6 @@
         }
 
         mMinutesText = mMinutesTexts;
-
-        final int hoursAlpha = mShowHours ? ALPHA_OPAQUE : ALPHA_TRANSPARENT;
-        mAlpha[HOURS].setValue(hoursAlpha);
-
-        final int minutesAlpha = mShowHours ? ALPHA_TRANSPARENT : ALPHA_OPAQUE;
-        mAlpha[MINUTES].setValue(minutesAlpha);
     }
 
     @Override
@@ -653,90 +628,144 @@
         final float alphaMod = mInputEnabled ? 1 : mDisabledAlpha;
 
         drawCircleBackground(canvas);
-        drawHours(canvas, alphaMod);
-        drawMinutes(canvas, alphaMod);
+
+        final Path selectorPath = mSelectorPath;
+        drawSelector(canvas, selectorPath);
+        drawHours(canvas, selectorPath, alphaMod);
+        drawMinutes(canvas, selectorPath, alphaMod);
         drawCenter(canvas, alphaMod);
     }
 
+    private void showPicker(boolean hours, boolean animate) {
+        if (mShowHours == hours) {
+            return;
+        }
+
+        mShowHours = hours;
+
+        if (animate) {
+            animatePicker(hours, ANIM_DURATION_NORMAL);
+        }
+
+        initData();
+        invalidate();
+        mTouchHelper.invalidateRoot();
+    }
+
+    private void animatePicker(boolean hoursToMinutes, long duration) {
+        final float target = hoursToMinutes ? HOURS : MINUTES;
+        if (mHoursToMinutes == target) {
+            // If we have a pending or running animator, cancel it.
+            if (mHoursToMinutesAnimator != null && mHoursToMinutesAnimator.isStarted()) {
+                mHoursToMinutesAnimator.cancel();
+                mHoursToMinutesAnimator = null;
+            }
+
+            // We're already showing the correct picker.
+            return;
+        }
+
+        mHoursToMinutesAnimator = ObjectAnimator.ofFloat(this, HOURS_TO_MINUTES, target);
+        mHoursToMinutesAnimator.setAutoCancel(true);
+        mHoursToMinutesAnimator.setDuration(duration);
+        mHoursToMinutesAnimator.start();
+    }
+
     private void drawCircleBackground(Canvas canvas) {
         canvas.drawCircle(mXCenter, mYCenter, mCircleRadius, mPaintBackground);
     }
 
-    private void drawHours(Canvas canvas, float alphaMod) {
-        final int hoursAlpha = (int) (mAlpha[HOURS].getValue() * alphaMod + 0.5f);
+    private void drawHours(Canvas canvas, Path selectorPath, float alphaMod) {
+        final int hoursAlpha = (int) (255f * (1f - mHoursToMinutes) * alphaMod + 0.5f);
         if (hoursAlpha > 0) {
-            // Draw the hour selector under the elements.
-            drawSelector(canvas, mIsOnInnerCircle ? HOURS_INNER : HOURS, null, alphaMod);
-
-            // Draw outer hours.
-            drawTextElements(canvas, mTextSize[HOURS], mTypeface, mTextColor[HOURS],
-                    mOuterTextHours, mOuterTextX[HOURS], mOuterTextY[HOURS], mPaint[HOURS],
-                    hoursAlpha, !mIsOnInnerCircle, mSelectionDegrees[HOURS], false);
-
-            // Draw inner hours (13-00) for 24-hour time.
-            if (mIs24HourMode && mInnerTextHours != null) {
-                drawTextElements(canvas, mTextSize[HOURS_INNER], mTypeface, mTextColor[HOURS_INNER],
-                        mInnerTextHours, mInnerTextX, mInnerTextY, mPaint[HOURS], hoursAlpha,
-                        mIsOnInnerCircle, mSelectionDegrees[HOURS], false);
-            }
-        }
-    }
-
-    private void drawMinutes(Canvas canvas, float alphaMod) {
-        final int minutesAlpha = (int) (mAlpha[MINUTES].getValue() * alphaMod + 0.5f);
-        if (minutesAlpha > 0) {
-            // Draw the minute selector under the elements.
-            drawSelector(canvas, MINUTES, mSelectorPath, alphaMod);
-
-            // Exclude the selector region, then draw minutes with no
+            // Exclude the selector region, then draw inner/outer hours with no
             // activated states.
             canvas.save(Canvas.CLIP_SAVE_FLAG);
-            canvas.clipPath(mSelectorPath, Region.Op.DIFFERENCE);
-            drawTextElements(canvas, mTextSize[MINUTES], mTypeface, mTextColor[MINUTES],
-                    mMinutesText, mOuterTextX[MINUTES], mOuterTextY[MINUTES], mPaint[MINUTES],
-                    minutesAlpha, false, 0, false);
+            canvas.clipPath(selectorPath, Region.Op.DIFFERENCE);
+            drawHoursClipped(canvas, hoursAlpha, false);
             canvas.restore();
 
             // Intersect the selector region, then draw minutes with only
             // activated states.
             canvas.save(Canvas.CLIP_SAVE_FLAG);
-            canvas.clipPath(mSelectorPath, Region.Op.INTERSECT);
-            drawTextElements(canvas, mTextSize[MINUTES], mTypeface, mTextColor[MINUTES],
-                    mMinutesText, mOuterTextX[MINUTES], mOuterTextY[MINUTES], mPaint[MINUTES],
-                    minutesAlpha, true, mSelectionDegrees[MINUTES], true);
+            canvas.clipPath(selectorPath, Region.Op.INTERSECT);
+            drawHoursClipped(canvas, hoursAlpha, true);
             canvas.restore();
         }
     }
 
+    private void drawHoursClipped(Canvas canvas, int hoursAlpha, boolean showActivated) {
+        // Draw outer hours.
+        drawTextElements(canvas, mTextSize[HOURS], mTypeface, mTextColor[HOURS], mOuterTextHours,
+                mOuterTextX[HOURS], mOuterTextY[HOURS], mPaint[HOURS], hoursAlpha,
+                showActivated && !mIsOnInnerCircle, mSelectionDegrees[HOURS], showActivated);
+
+        // Draw inner hours (13-00) for 24-hour time.
+        if (mIs24HourMode && mInnerTextHours != null) {
+            drawTextElements(canvas, mTextSize[HOURS_INNER], mTypeface, mTextColor[HOURS_INNER],
+                    mInnerTextHours, mInnerTextX, mInnerTextY, mPaint[HOURS], hoursAlpha,
+                    showActivated && mIsOnInnerCircle, mSelectionDegrees[HOURS], showActivated);
+        }
+    }
+
+    private void drawMinutes(Canvas canvas, Path selectorPath, float alphaMod) {
+        final int minutesAlpha = (int) (255f * mHoursToMinutes * alphaMod + 0.5f);
+        if (minutesAlpha > 0) {
+            // Exclude the selector region, then draw minutes with no
+            // activated states.
+            canvas.save(Canvas.CLIP_SAVE_FLAG);
+            canvas.clipPath(selectorPath, Region.Op.DIFFERENCE);
+            drawMinutesClipped(canvas, minutesAlpha, false);
+            canvas.restore();
+
+            // Intersect the selector region, then draw minutes with only
+            // activated states.
+            canvas.save(Canvas.CLIP_SAVE_FLAG);
+            canvas.clipPath(selectorPath, Region.Op.INTERSECT);
+            drawMinutesClipped(canvas, minutesAlpha, true);
+            canvas.restore();
+        }
+    }
+
+    private void drawMinutesClipped(Canvas canvas, int minutesAlpha, boolean showActivated) {
+        drawTextElements(canvas, mTextSize[MINUTES], mTypeface, mTextColor[MINUTES], mMinutesText,
+                mOuterTextX[MINUTES], mOuterTextY[MINUTES], mPaint[MINUTES], minutesAlpha,
+                showActivated, mSelectionDegrees[MINUTES], showActivated);
+    }
+
     private void drawCenter(Canvas canvas, float alphaMod) {
         mPaintCenter.setAlpha((int) (255 * alphaMod + 0.5f));
         canvas.drawCircle(mXCenter, mYCenter, mCenterDotRadius, mPaintCenter);
     }
 
-    private int applyAlpha(int argb, int alpha) {
-        final int srcAlpha = (argb >> 24) & 0xFF;
-        final int dstAlpha = (int) (srcAlpha * (alpha / 255.0) + 0.5f);
-        return (0xFFFFFF & argb) | (dstAlpha << 24);
-    }
-
     private int getMultipliedAlpha(int argb, int alpha) {
         return (int) (Color.alpha(argb) * (alpha / 255.0) + 0.5);
     }
 
-    private void drawSelector(Canvas canvas, int index, Path selectorPath, float alphaMod) {
-        final int alpha = (int) (mAlpha[index % 2].getValue() * alphaMod + 0.5f);
-        final int color = applyAlpha(mSelectorColor, alpha);
+    private void drawSelector(Canvas canvas, Path selectorPath) {
+        // Determine the current length, angle, and dot scaling factor.
+        final int hoursIndex = mIsOnInnerCircle ? HOURS_INNER : HOURS;
+        final int hoursInset = mTextInset[hoursIndex];
+        final int hoursAngleDeg = mSelectionDegrees[hoursIndex % 2];
+        final float hoursDotScale = mSelectionDegrees[hoursIndex % 2] % 30 != 0 ? 1 : 0;
+
+        final int minutesIndex = MINUTES;
+        final int minutesInset = mTextInset[minutesIndex];
+        final int minutesAngleDeg = mSelectionDegrees[minutesIndex];
+        final float minutesDotScale = mSelectionDegrees[minutesIndex] % 30 != 0 ? 1 : 0;
 
         // Calculate the current radius at which to place the selection circle.
         final int selRadius = mSelectorRadius;
-        final int selLength = mCircleRadius - mTextInset[index];
-        final double selAngleRad = Math.toRadians(mSelectionDegrees[index % 2]);
+        final float selLength =
+                mCircleRadius - MathUtils.lerp(hoursInset, minutesInset, mHoursToMinutes);
+        final double selAngleRad =
+                Math.toRadians(MathUtils.lerpDeg(hoursAngleDeg, minutesAngleDeg, mHoursToMinutes));
         final float selCenterX = mXCenter + selLength * (float) Math.sin(selAngleRad);
         final float selCenterY = mYCenter - selLength * (float) Math.cos(selAngleRad);
 
         // Draw the selection circle.
-        final Paint paint = mPaintSelector[index % 2][SELECTOR_CIRCLE];
-        paint.setColor(color);
+        final Paint paint = mPaintSelector[SELECTOR_CIRCLE];
+        paint.setColor(mSelectorColor);
         canvas.drawCircle(selCenterX, selCenterY, selRadius, paint);
 
         // If needed, set up the clip path for later.
@@ -746,26 +775,26 @@
         }
 
         // Draw the dot if we're between two items.
-        final boolean shouldDrawDot = mSelectionDegrees[index % 2] % 30 != 0;
-        if (shouldDrawDot) {
-            final Paint dotPaint = mPaintSelector[index % 2][SELECTOR_DOT];
+        final float dotScale = MathUtils.lerp(hoursDotScale, minutesDotScale, mHoursToMinutes);
+        if (dotScale > 0) {
+            final Paint dotPaint = mPaintSelector[SELECTOR_DOT];
             dotPaint.setColor(mSelectorDotColor);
-            canvas.drawCircle(selCenterX, selCenterY, mSelectorDotRadius, dotPaint);
+            canvas.drawCircle(selCenterX, selCenterY, mSelectorDotRadius * dotScale, dotPaint);
         }
 
         // Shorten the line to only go from the edge of the center dot to the
         // edge of the selection circle.
         final double sin = Math.sin(selAngleRad);
         final double cos = Math.cos(selAngleRad);
-        final int lineLength = selLength - selRadius;
+        final float lineLength = selLength - selRadius;
         final int centerX = mXCenter + (int) (mCenterDotRadius * sin);
         final int centerY = mYCenter - (int) (mCenterDotRadius * cos);
         final float linePointX = centerX + (int) (lineLength * sin);
         final float linePointY = centerY - (int) (lineLength * cos);
 
         // Draw the line.
-        final Paint linePaint = mPaintSelector[index % 2][SELECTOR_LINE];
-        linePaint.setColor(color);
+        final Paint linePaint = mPaintSelector[SELECTOR_LINE];
+        linePaint.setColor(mSelectorColor);
         linePaint.setStrokeWidth(mSelectorStroke);
         canvas.drawLine(mXCenter, mYCenter, linePointX, linePointY, linePaint);
     }
@@ -842,73 +871,6 @@
         }
     }
 
-    private static ObjectAnimator getFadeOutAnimator(IntHolder target, int startAlpha, int endAlpha,
-                InvalidateUpdateListener updateListener) {
-        final ObjectAnimator animator = ObjectAnimator.ofInt(target, "value", startAlpha, endAlpha);
-        animator.setDuration(FADE_OUT_DURATION);
-        animator.addUpdateListener(updateListener);
-        return animator;
-    }
-
-    private static ObjectAnimator getFadeInAnimator(IntHolder target, int startAlpha, int endAlpha,
-                InvalidateUpdateListener updateListener) {
-        final float delayMultiplier = 0.25f;
-        final float transitionDurationMultiplier = 1f;
-        final float totalDurationMultiplier = transitionDurationMultiplier + delayMultiplier;
-        final int totalDuration = (int) (FADE_IN_DURATION * totalDurationMultiplier);
-        final float delayPoint = (delayMultiplier * FADE_IN_DURATION) / totalDuration;
-
-        final Keyframe kf0, kf1, kf2;
-        kf0 = Keyframe.ofInt(0f, startAlpha);
-        kf1 = Keyframe.ofInt(delayPoint, startAlpha);
-        kf2 = Keyframe.ofInt(1f, endAlpha);
-        final PropertyValuesHolder fadeIn = PropertyValuesHolder.ofKeyframe("value", kf0, kf1, kf2);
-
-        final ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(target, fadeIn);
-        animator.setDuration(totalDuration);
-        animator.addUpdateListener(updateListener);
-        return animator;
-    }
-
-    private class InvalidateUpdateListener implements ValueAnimator.AnimatorUpdateListener {
-        @Override
-        public void onAnimationUpdate(ValueAnimator animation) {
-            RadialTimePickerView.this.invalidate();
-        }
-    }
-
-    private void startHoursToMinutesAnimation() {
-        if (mHoursToMinutesAnims.size() == 0) {
-            mHoursToMinutesAnims.add(getFadeOutAnimator(mAlpha[HOURS],
-                    ALPHA_OPAQUE, ALPHA_TRANSPARENT, mInvalidateUpdateListener));
-            mHoursToMinutesAnims.add(getFadeInAnimator(mAlpha[MINUTES],
-                    ALPHA_TRANSPARENT, ALPHA_OPAQUE, mInvalidateUpdateListener));
-        }
-
-        if (mTransition != null && mTransition.isRunning()) {
-            mTransition.end();
-        }
-        mTransition = new AnimatorSet();
-        mTransition.playTogether(mHoursToMinutesAnims);
-        mTransition.start();
-    }
-
-    private void startMinutesToHoursAnimation() {
-        if (mMinuteToHoursAnims.size() == 0) {
-            mMinuteToHoursAnims.add(getFadeOutAnimator(mAlpha[MINUTES],
-                    ALPHA_OPAQUE, ALPHA_TRANSPARENT, mInvalidateUpdateListener));
-            mMinuteToHoursAnims.add(getFadeInAnimator(mAlpha[HOURS],
-                    ALPHA_TRANSPARENT, ALPHA_OPAQUE, mInvalidateUpdateListener));
-        }
-
-        if (mTransition != null && mTransition.isRunning()) {
-            mTransition.end();
-        }
-        mTransition = new AnimatorSet();
-        mTransition.playTogether(mMinuteToHoursAnims);
-        mTransition.start();
-    }
-
     private int getDegreesFromXY(float x, float y, boolean constrainOutside) {
         // Ensure the point is inside the touchable area.
         final int innerBound;
@@ -992,6 +954,9 @@
             return false;
         }
 
+        // Ensure we're showing the correct picker.
+        animatePicker(mShowHours, ANIM_DURATION_TOUCH);
+
         final int type;
         final int newValue;
         final boolean valueChanged;
@@ -1356,20 +1321,4 @@
             return id >>> SHIFT_VALUE & MASK_VALUE;
         }
     }
-
-    private static class IntHolder {
-        private int mValue;
-
-        public IntHolder(int value) {
-            mValue = value;
-        }
-
-        public void setValue(int value) {
-            mValue = value;
-        }
-
-        public int getValue() {
-            return mValue;
-        }
-    }
 }
diff --git a/core/java/android/widget/TimePickerClockDelegate.java b/core/java/android/widget/TimePickerClockDelegate.java
index 61ef6dc..6d41c1d 100644
--- a/core/java/android/widget/TimePickerClockDelegate.java
+++ b/core/java/android/widget/TimePickerClockDelegate.java
@@ -200,8 +200,8 @@
 
         a.recycle();
 
-        mRadialTimePickerView = (RadialTimePickerView) mainView.findViewById(
-                R.id.radial_picker);
+        mRadialTimePickerView = (RadialTimePickerView) mainView.findViewById(R.id.radial_picker);
+        mRadialTimePickerView.applyAttributes(attrs, defStyleAttr, defStyleRes);
 
         setupListeners();
 
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index f0e216f..255e23a 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.app;
 
+import android.animation.ObjectAnimator;
+import android.annotation.NonNull;
 import android.app.Activity;
 import android.content.ComponentName;
 import android.content.Context;
@@ -29,6 +31,7 @@
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ResolveInfo;
 import android.database.DataSetObserver;
+import android.graphics.Color;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.Icon;
 import android.os.Bundle;
@@ -46,13 +49,18 @@
 import android.service.chooser.IChooserTargetResult;
 import android.service.chooser.IChooserTargetService;
 import android.text.TextUtils;
+import android.util.FloatProperty;
 import android.util.Log;
 import android.util.Slog;
 import android.view.LayoutInflater;
 import android.view.View;
+import android.view.View.MeasureSpec;
 import android.view.View.OnClickListener;
 import android.view.View.OnLongClickListener;
 import android.view.ViewGroup;
+import android.view.ViewGroup.LayoutParams;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
 import android.widget.AbsListView;
 import android.widget.BaseAdapter;
 import android.widget.ListView;
@@ -80,6 +88,7 @@
     private Intent mReferrerFillInIntent;
 
     private ChooserListAdapter mChooserListAdapter;
+    private ChooserRowAdapter mChooserRowAdapter;
 
     private final List<ChooserTargetServiceConnection> mServiceConnections = new ArrayList<>();
 
@@ -253,7 +262,9 @@
             boolean alwaysUseOption) {
         final ListView listView = adapterView instanceof ListView ? (ListView) adapterView : null;
         mChooserListAdapter = (ChooserListAdapter) adapter;
-        adapterView.setAdapter(new ChooserRowAdapter(mChooserListAdapter));
+        mChooserRowAdapter = new ChooserRowAdapter(mChooserListAdapter);
+        mChooserRowAdapter.registerDataSetObserver(new OffsetDataSetObserver(adapterView));
+        adapterView.setAdapter(mChooserRowAdapter);
         if (listView != null) {
             listView.setItemsCanFocus(true);
         }
@@ -913,19 +924,103 @@
         }
     }
 
+    static class RowScale {
+        private static final int DURATION = 400;
+
+        float mScale;
+        ChooserRowAdapter mAdapter;
+        private final ObjectAnimator mAnimator;
+
+        public static final FloatProperty<RowScale> PROPERTY =
+                new FloatProperty<RowScale>("scale") {
+            @Override
+            public void setValue(RowScale object, float value) {
+                object.mScale = value;
+                object.mAdapter.notifyDataSetChanged();
+            }
+
+            @Override
+            public Float get(RowScale object) {
+                return object.mScale;
+            }
+        };
+
+        public RowScale(@NonNull ChooserRowAdapter adapter, float from, float to) {
+            mAdapter = adapter;
+            mScale = from;
+            if (from == to) {
+                mAnimator = null;
+                return;
+            }
+
+            mAnimator = ObjectAnimator.ofFloat(this, PROPERTY, from, to).setDuration(DURATION);
+        }
+
+        public RowScale setInterpolator(Interpolator interpolator) {
+            if (mAnimator != null) {
+                mAnimator.setInterpolator(interpolator);
+            }
+            return this;
+        }
+
+        public float get() {
+            return mScale;
+        }
+
+        public void startAnimation() {
+            if (mAnimator != null) {
+                mAnimator.start();
+            }
+        }
+
+        public void cancelAnimation() {
+            if (mAnimator != null) {
+                mAnimator.cancel();
+            }
+        }
+    }
+
     class ChooserRowAdapter extends BaseAdapter {
         private ChooserListAdapter mChooserListAdapter;
         private final LayoutInflater mLayoutInflater;
         private final int mColumnCount = 4;
+        private RowScale[] mServiceTargetScale;
+        private final Interpolator mInterpolator;
 
         public ChooserRowAdapter(ChooserListAdapter wrappedAdapter) {
             mChooserListAdapter = wrappedAdapter;
             mLayoutInflater = LayoutInflater.from(ChooserActivity.this);
 
+            mInterpolator = AnimationUtils.loadInterpolator(ChooserActivity.this,
+                    android.R.interpolator.decelerate_quint);
+
             wrappedAdapter.registerDataSetObserver(new DataSetObserver() {
                 @Override
                 public void onChanged() {
                     super.onChanged();
+                    final int rcount = getServiceTargetRowCount();
+                    if (mServiceTargetScale == null
+                            || mServiceTargetScale.length != rcount) {
+                        RowScale[] old = mServiceTargetScale;
+                        int oldRCount = old != null ? old.length : 0;
+                        mServiceTargetScale = new RowScale[rcount];
+                        if (old != null && rcount > 0) {
+                            System.arraycopy(old, 0, mServiceTargetScale, 0,
+                                    Math.min(old.length, rcount));
+                        }
+
+                        for (int i = rcount; i < oldRCount; i++) {
+                            old[i].cancelAnimation();
+                        }
+
+                        for (int i = oldRCount; i < rcount; i++) {
+                            final RowScale rs = new RowScale(ChooserRowAdapter.this, 0.f, 1.f)
+                                    .setInterpolator(mInterpolator);
+                            mServiceTargetScale[i] = rs;
+                            rs.startAnimation();
+                        }
+                    }
+
                     notifyDataSetChanged();
                 }
 
@@ -933,19 +1028,43 @@
                 public void onInvalidated() {
                     super.onInvalidated();
                     notifyDataSetInvalidated();
+                    if (mServiceTargetScale != null) {
+                        for (RowScale rs : mServiceTargetScale) {
+                            rs.cancelAnimation();
+                        }
+                    }
                 }
             });
         }
 
+        private float getRowScale(int rowPosition) {
+            final int start = getCallerTargetRowCount();
+            final int end = start + getServiceTargetRowCount();
+            if (rowPosition >= start && rowPosition < end) {
+                return mServiceTargetScale[rowPosition - start].get();
+            }
+            return 1.f;
+        }
+
         @Override
         public int getCount() {
             return (int) (
-                    Math.ceil((float) mChooserListAdapter.getCallerTargetCount() / mColumnCount)
-                    + Math.ceil((float) mChooserListAdapter.getServiceTargetCount() / mColumnCount)
+                    getCallerTargetRowCount()
+                    + getServiceTargetRowCount()
                     + Math.ceil((float) mChooserListAdapter.getStandardTargetCount() / mColumnCount)
             );
         }
 
+        public int getCallerTargetRowCount() {
+            return (int) Math.ceil(
+                    (float) mChooserListAdapter.getCallerTargetCount() / mColumnCount);
+        }
+
+        public int getServiceTargetRowCount() {
+            return (int) Math.ceil(
+                    (float) mChooserListAdapter.getServiceTargetCount() / mColumnCount);
+        }
+
         @Override
         public Object getItem(int position) {
             // We have nothing useful to return here.
@@ -959,33 +1078,67 @@
 
         @Override
         public View getView(int position, View convertView, ViewGroup parent) {
-            final View[] holder;
+            final RowViewHolder holder;
             if (convertView == null) {
                 holder = createViewHolder(parent);
             } else {
-                holder = (View[]) convertView.getTag();
+                holder = (RowViewHolder) convertView.getTag();
             }
             bindViewHolder(position, holder);
 
-            // We keep the actual list item view as the last item in the holder array
-            return holder[mColumnCount];
+            return holder.row;
         }
 
-        View[] createViewHolder(ViewGroup parent) {
-            final View[] holder = new View[mColumnCount + 1];
-
+        RowViewHolder createViewHolder(ViewGroup parent) {
             final ViewGroup row = (ViewGroup) mLayoutInflater.inflate(R.layout.chooser_row,
                     parent, false);
+            final RowViewHolder holder = new RowViewHolder(row, mColumnCount);
+            final int spec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+
             for (int i = 0; i < mColumnCount; i++) {
-                holder[i] = mChooserListAdapter.createView(row);
-                row.addView(holder[i]);
+                final View v = mChooserListAdapter.createView(row);
+                v.setOnClickListener(new OnClickListener() {
+                    @Override
+                    public void onClick(View v) {
+                        startSelected(holder.itemIndex, false, true);
+                    }
+                });
+                v.setOnLongClickListener(new OnLongClickListener() {
+                    @Override
+                    public boolean onLongClick(View v) {
+                        showAppDetails(
+                                mChooserListAdapter.resolveInfoForPosition(holder.itemIndex, true));
+                        return true;
+                    }
+                });
+                row.addView(v);
+                holder.cells[i] = v;
+
+                // Force height to be a given so we don't have visual disruption during scaling.
+                LayoutParams lp = v.getLayoutParams();
+                v.measure(spec, spec);
+                if (lp == null) {
+                    lp = new LayoutParams(LayoutParams.MATCH_PARENT, v.getMeasuredHeight());
+                    row.setLayoutParams(lp);
+                } else {
+                    lp.height = v.getMeasuredHeight();
+                }
+            }
+
+            // Pre-measure so we can scale later.
+            holder.measure();
+            LayoutParams lp = row.getLayoutParams();
+            if (lp == null) {
+                lp = new LayoutParams(LayoutParams.MATCH_PARENT, holder.measuredRowHeight);
+                row.setLayoutParams(lp);
+            } else {
+                lp.height = holder.measuredRowHeight;
             }
             row.setTag(holder);
-            holder[mColumnCount] = row;
             return holder;
         }
 
-        void bindViewHolder(int rowPosition, View[] holder) {
+        void bindViewHolder(int rowPosition, RowViewHolder holder) {
             final int start = getFirstRowPosition(rowPosition);
             final int startType = mChooserListAdapter.getPositionTargetType(start);
 
@@ -994,34 +1147,26 @@
                 end--;
             }
 
-            final ViewGroup row = (ViewGroup) holder[mColumnCount];
-
             if (startType == ChooserListAdapter.TARGET_SERVICE) {
-                row.setBackgroundColor(getColor(R.color.chooser_service_row_background_color));
+                holder.row.setBackgroundColor(
+                        getColor(R.color.chooser_service_row_background_color));
             } else {
-                row.setBackground(null);
+                holder.row.setBackgroundColor(Color.TRANSPARENT);
+            }
+
+            final int oldHeight = holder.row.getLayoutParams().height;
+            holder.row.getLayoutParams().height = Math.max(1,
+                    (int) (holder.measuredRowHeight * getRowScale(rowPosition)));
+            if (holder.row.getLayoutParams().height != oldHeight) {
+                holder.row.requestLayout();
             }
 
             for (int i = 0; i < mColumnCount; i++) {
-                final View v = holder[i];
+                final View v = holder.cells[i];
                 if (start + i <= end) {
                     v.setVisibility(View.VISIBLE);
-                    final int itemIndex = start + i;
-                    mChooserListAdapter.bindView(itemIndex, v);
-                    v.setOnClickListener(new OnClickListener() {
-                        @Override
-                        public void onClick(View v) {
-                            startSelected(itemIndex, false, true);
-                        }
-                    });
-                    v.setOnLongClickListener(new OnLongClickListener() {
-                        @Override
-                        public boolean onLongClick(View v) {
-                            showAppDetails(
-                                    mChooserListAdapter.resolveInfoForPosition(itemIndex, true));
-                            return true;
-                        }
-                    });
+                    holder.itemIndex = start + i;
+                    mChooserListAdapter.bindView(holder.itemIndex, v);
                 } else {
                     v.setVisibility(View.GONE);
                 }
@@ -1048,6 +1193,24 @@
         }
     }
 
+    static class RowViewHolder {
+        final View[] cells;
+        final ViewGroup row;
+        int measuredRowHeight;
+        int itemIndex;
+
+        public RowViewHolder(ViewGroup row, int cellCount) {
+            this.row = row;
+            this.cells = new View[cellCount];
+        }
+
+        public void measure() {
+            final int spec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+            row.measure(spec, spec);
+            measuredRowHeight = row.getMeasuredHeight();
+        }
+    }
+
     static class ChooserTargetServiceConnection implements ServiceConnection {
         private final DisplayResolveInfo mOriginalTarget;
         private ComponentName mConnectedComponent;
@@ -1199,4 +1362,44 @@
             mSelectedTarget = null;
         }
     }
+
+    class OffsetDataSetObserver extends DataSetObserver {
+        private final AbsListView mListView;
+        private int mCachedViewType = -1;
+        private View mCachedView;
+
+        public OffsetDataSetObserver(AbsListView listView) {
+            mListView = listView;
+        }
+
+        @Override
+        public void onChanged() {
+            if (mResolverDrawerLayout == null) {
+                return;
+            }
+
+            final int chooserTargetRows = mChooserRowAdapter.getServiceTargetRowCount();
+            int offset = 0;
+            for (int i = 0; i < chooserTargetRows; i++)  {
+                final int pos = mChooserRowAdapter.getCallerTargetRowCount() + i;
+                final int vt = mChooserRowAdapter.getItemViewType(pos);
+                if (vt != mCachedViewType) {
+                    mCachedView = null;
+                }
+                final View v = mChooserRowAdapter.getView(pos, mCachedView, mListView);
+                int height = ((RowViewHolder) (v.getTag())).measuredRowHeight;
+
+                offset += (int) (height * mChooserRowAdapter.getRowScale(pos) * chooserTargetRows);
+
+                if (vt >= 0) {
+                    mCachedViewType = vt;
+                    mCachedView = v;
+                } else {
+                    mCachedViewType = -1;
+                }
+            }
+
+            mResolverDrawerLayout.setCollapsibleHeightReserved(offset);
+        }
+    }
 }
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 1710489..ba0912a 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -103,6 +103,8 @@
     private ResolverComparator mResolverComparator;
     private PickTargetOptionRequest mPickOptionRequest;
 
+    protected ResolverDrawerLayout mResolverDrawerLayout;
+
     private boolean mRegistered;
     private final PackageMonitor mPackageMonitor = new PackageMonitor() {
         @Override public void onSomePackagesChanged() {
@@ -253,6 +255,7 @@
             if (isVoiceInteraction()) {
                 rdl.setCollapsed(false);
             }
+            mResolverDrawerLayout = rdl;
         }
 
         if (title == null) {
@@ -1570,7 +1573,10 @@
 
         private void onBindView(View view, TargetInfo info) {
             final ViewHolder holder = (ViewHolder) view.getTag();
-            holder.text.setText(info.getDisplayLabel());
+            final CharSequence label = info.getDisplayLabel();
+            if (!TextUtils.equals(holder.text.getText(), label)) {
+                holder.text.setText(info.getDisplayLabel());
+            }
             if (showsExtendedInfo(info)) {
                 holder.text2.setVisibility(View.VISIBLE);
                 holder.text2.setText(info.getExtendedInfo());
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index b6d7364..3ff12ad 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -488,8 +488,14 @@
     }
 
     public void clearContentView() {
-        if (mNonClientDecorView.getChildCount() > 1) {
-            mNonClientDecorView.removeViewAt(1);
+        if (mNonClientDecorView != null) {
+            if (mNonClientDecorView.getChildCount() > 1) {
+                mNonClientDecorView.removeViewAt(1);
+            }
+        } else {
+            // This window doesn't have non client decor, so we need to just remove the children
+            // of the decor view.
+            mDecor.removeAllViews();
         }
     }
 
@@ -2615,7 +2621,7 @@
             if (action == MotionEvent.ACTION_DOWN) {
                 int y = (int)event.getY();
                 if (y >= (getHeight()-5) && !mWindow.hasChildren()) {
-                    Log.i(TAG, "Watchiing!");
+                    Log.i(TAG, "Watching!");
                     mWatchingForMenu = true;
                 }
                 return false;
@@ -5413,16 +5419,10 @@
      * @Return Returns true if the window should show a shadow.
      **/
     private boolean nonClientDecorHasShadow(int workspaceId) {
-        // TODO(skuhne): Add side by side mode here to add a decor.
         return workspaceId == FREEFORM_WORKSPACE_STACK_ID;
     }
 
     @Override
-    public boolean hasNonClientDecorView() {
-        return mNonClientDecorView != null;
-    }
-
-    @Override
     public void setTheme(int resid) {
         mTheme = resid;
         if (mDecor != null) {
diff --git a/core/java/com/android/internal/view/BaseIWindow.java b/core/java/com/android/internal/view/BaseIWindow.java
index 3eeabcd..07bfce7 100644
--- a/core/java/com/android/internal/view/BaseIWindow.java
+++ b/core/java/com/android/internal/view/BaseIWindow.java
@@ -100,14 +100,6 @@
     }
 
     @Override
-    public void onAnimationStarted(int remainingFrameCount) {
-    }
-
-    @Override
-    public void onAnimationStopped() {
-    }
-
-    @Override
     public void dispatchWindowShown() {
     }
 }
diff --git a/core/java/com/android/internal/widget/NonClientDecorView.java b/core/java/com/android/internal/widget/NonClientDecorView.java
index 56cf921..da43723 100644
--- a/core/java/com/android/internal/widget/NonClientDecorView.java
+++ b/core/java/com/android/internal/widget/NonClientDecorView.java
@@ -17,15 +17,22 @@
 package com.android.internal.widget;
 
 import android.content.Context;
+import android.graphics.Color;
 import android.graphics.Rect;
+import android.os.Looper;
 import android.os.RemoteException;
 import android.util.AttributeSet;
+import android.view.Choreographer;
+import android.view.DisplayListCanvas;
 import android.view.MotionEvent;
+import android.view.RenderNode;
+import android.view.ThreadedRenderer;
 import android.view.View;
 import android.widget.LinearLayout;
 import android.view.ViewGroup;
 import android.view.ViewOutlineProvider;
 import android.view.Window;
+import android.view.WindowCallbacks;
 import android.util.Log;
 import android.util.TypedValue;
 
@@ -58,7 +65,7 @@
  * This will be mitigated once b/22527834 will be addressed.
  */
 public class NonClientDecorView extends LinearLayout
-        implements View.OnClickListener, View.OnTouchListener {
+        implements View.OnClickListener, View.OnTouchListener, WindowCallbacks {
     private final static String TAG = "NonClientDecorView";
     // The height of a window which has focus in DIP.
     private final int DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP = 20;
@@ -67,6 +74,8 @@
     private PhoneWindow mOwner = null;
     private boolean mWindowHasShadow = false;
     private boolean mShowDecor = false;
+    // True when this object is listening for window size changes.
+    private boolean mAttachedCallbacksToRootViewImpl = false;
 
     // True if the window is being dragged.
     private boolean mDragging = false;
@@ -85,6 +94,9 @@
     // to max until the first layout command has been executed.
     private boolean mAllowUpdateElevation = false;
 
+    // The resize frame renderer.
+    private ResizeFrameThread mFrameRendererThread = null;
+
     public NonClientDecorView(Context context) {
         super(context);
     }
@@ -97,6 +109,30 @@
         super(context, attrs, defStyle);
     }
 
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        if (!mAttachedCallbacksToRootViewImpl) {
+            // If there is no window callback installed there was no window set before. Set it now.
+            // Note that our ViewRootImpl object will not change.
+            getViewRootImpl().addWindowCallbacks(this);
+            mAttachedCallbacksToRootViewImpl = true;
+        } else if (mFrameRendererThread != null) {
+            // We are resizing and this call happened due to a configuration change. Tell the
+            // renderer about it.
+            mFrameRendererThread.onConfigurationChange();
+        }
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        if (mAttachedCallbacksToRootViewImpl) {
+            getViewRootImpl().removeWindowCallbacks(this);
+            mAttachedCallbacksToRootViewImpl = false;
+        }
+    }
+
     public void setPhoneWindow(PhoneWindow owner, boolean showDecor, boolean windowHasShadow) {
         mOwner = owner;
         mWindowHasShadow = windowHasShadow;
@@ -108,6 +144,7 @@
         // By changing the outline provider to BOUNDS, the window can remove its
         // background without removing the shadow.
         mOwner.getDecorView().setOutlineProvider(ViewOutlineProvider.BOUNDS);
+
         findViewById(R.id.maximize_window).setOnClickListener(this);
         findViewById(R.id.close_window).setOnClickListener(this);
     }
@@ -251,7 +288,9 @@
      **/
     private void updateElevation() {
         float elevation = 0;
-        if (mWindowHasShadow) {
+        // Do not use a shadow when we are in resizing mode (mRenderer not null) since the shadow
+        // is bound to the content size and not the target size.
+        if (mWindowHasShadow && mFrameRendererThread == null) {
             boolean fill = isFillingScreen();
             elevation = fill ? 0 :
                     (mWindowHasFocus ? DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP :
@@ -293,4 +332,300 @@
             }
         }
     }
+
+    @Override
+    public void onWindowDragResizeStart(Rect initialBounds) {
+        if (mOwner.isDestroyed()) {
+            // If the owner's window is gone, we should not be able to come here anymore.
+            releaseResources();
+            return;
+        }
+        if (mFrameRendererThread != null) {
+            return;
+        }
+        final ThreadedRenderer renderer =
+                (ThreadedRenderer) mOwner.getDecorView().getHardwareRenderer();
+        if (renderer != null) {
+            mFrameRendererThread = new ResizeFrameThread(renderer, initialBounds);
+            // Get rid of the shadow while we are resizing. Shadow drawing takes considerable time.
+            // If we want to get the shadow shown while resizing, we would need to elevate a new
+            // element which owns the caption and has the elevation.
+            updateElevation();
+        }
+    }
+
+    @Override
+    public void onContentDraw(int xOffset, int yOffset, int xSize, int ySize) {
+        if (mFrameRendererThread != null) {
+            mFrameRendererThread.onContentDraw(xOffset, yOffset, xSize, ySize);
+        }
+    }
+
+    @Override
+    public void onWindowDragResizeEnd() {
+        releaseThreadedRenderer();
+    }
+
+    @Override
+    public void onWindowSizeIsChanging(Rect newBounds) {
+        if (mFrameRendererThread != null) {
+            mFrameRendererThread.setTargetRect(newBounds);
+        }
+    }
+
+    /**
+     * Release the renderer thread which is usually done when the user stops resizing.
+     */
+    private void releaseThreadedRenderer() {
+        if (mFrameRendererThread != null) {
+            mFrameRendererThread.releaseRenderer();
+            mFrameRendererThread = null;
+            // Bring the shadow back.
+            updateElevation();
+        }
+    }
+
+    /**
+     * Called when the parent window is destroyed to release all resources. Note that this will also
+     * destroy the renderer thread.
+     */
+    private void releaseResources() {
+        releaseThreadedRenderer();
+    }
+
+    /**
+     * The thread which draws the chrome while we are resizing.
+     * It starts with the creation and it ends once someone calls destroy().
+     * Any size changes can be passed by a call to setTargetRect will passed to the thread and
+     * executed via the Choreographer.
+     */
+    private class ResizeFrameThread extends Thread implements Choreographer.FrameCallback {
+        // This is containing the last requested size by a resize command. Note that this size might
+        // or might not have been applied to the output already.
+        private final Rect mTargetRect = new Rect();
+
+        // The render nodes for the multi threaded renderer.
+        private ThreadedRenderer mRenderer;
+        private RenderNode mFrameNode;
+        private RenderNode mBackdropNode;
+
+        private final Rect mOldTargetRect = new Rect();
+        private final Rect mNewTargetRect = new Rect();
+        private Choreographer mChoreographer;
+
+        // Cached size values from the last render for the case that the view hierarchy is gone
+        // during a configuration change.
+        private int mLastContentWidth;
+        private int mLastContentHeight;
+        private int mLastCaptionHeight;
+        private int mLastXOffset;
+        private int mLastYOffset;
+
+        ResizeFrameThread(ThreadedRenderer renderer, Rect initialBounds) {
+            setName("ResizeFrame");
+            mRenderer = renderer;
+
+            // Create the render nodes for our frame and backdrop which can be resized independently
+            // from the content.
+            mFrameNode = RenderNode.create("FrameNode", null);
+            mBackdropNode = RenderNode.create("BackdropNode", null);
+
+            mRenderer.addRenderNode(mFrameNode, false);
+            mRenderer.addRenderNode(mBackdropNode, true);
+
+            // Set the initial bounds and draw once so that we do not get a broken frame.
+            mTargetRect.set(initialBounds);
+            synchronized (this) {
+                changeWindowSizeLocked(initialBounds);
+            }
+
+            // Kick off our draw thread.
+            start();
+        }
+
+        /**
+         * Call this function asynchronously when the window size has been changed. The change will
+         * be picked up once per frame and the frame will be re-rendered accordingly.
+         * @param newTargetBounds The new target bounds.
+         */
+        public void setTargetRect(Rect newTargetBounds) {
+            synchronized (this) {
+                mTargetRect.set(newTargetBounds);
+                // Notify of a bounds change.
+                pingRenderLocked();
+            }
+        }
+
+        /**
+         * The window got replaced due to a configuration change.
+         */
+        public void onConfigurationChange() {
+            synchronized (this) {
+                if (mRenderer != null) {
+                    // Enforce a window redraw.
+                    mOldTargetRect.set(0, 0, 0, 0);
+                    pingRenderLocked();
+                }
+            }
+        }
+
+        /**
+         * All resources of the renderer will be released. This function can be called from the
+         * the UI thread as well as the renderer thread.
+         */
+        public void releaseRenderer() {
+            synchronized (this) {
+                if (mRenderer != null) {
+                    // Invalidate the current content bounds.
+                    mRenderer.setContentDrawBounds(0, 0, 0, 0);
+
+                    // Remove the render nodes again
+                    // (see comment above - better to do that only once).
+                    mRenderer.removeRenderNode(mFrameNode);
+                    mRenderer.removeRenderNode(mBackdropNode);
+
+                    mRenderer = null;
+
+                    // Exit the renderer loop.
+                    pingRenderLocked();
+                }
+            }
+        }
+
+        @Override
+        public void run() {
+            try {
+                Looper.prepare();
+                mChoreographer = Choreographer.getInstance();
+                Looper.loop();
+            } finally {
+                releaseRenderer();
+            }
+            synchronized (this) {
+                // Make sure no more messages are being sent.
+                mChoreographer = null;
+            }
+        }
+
+        /**
+         * The implementation of the FrameCallback.
+         * @param frameTimeNanos The time in nanoseconds when the frame started being rendered,
+         * in the {@link System#nanoTime()} timebase.  Divide this value by {@code 1000000}
+         */
+        @Override
+        public void doFrame(long frameTimeNanos) {
+            synchronized (this) {
+                if (mRenderer == null) {
+                    // Tell the looper to stop. We are done.
+                    Looper.myLooper().quit();
+                    return;
+                }
+                mNewTargetRect.set(mTargetRect);
+                if (!mNewTargetRect.equals(mOldTargetRect)) {
+                    mOldTargetRect.set(mNewTargetRect);
+                    changeWindowSizeLocked(mNewTargetRect);
+                }
+            }
+        }
+
+        /**
+         * The content is about to be drawn and we got the location of where it will be shown.
+         * If a "changeWindowSizeLocked" call has already been processed, we will re-issue the call
+         * if the previous call was ignored since the size was unknown.
+         * @param xOffset The x offset where the content is drawn to.
+         * @param yOffset The y offset where the content is drawn to.
+         * @param xSize The width size of the content. This should not be 0.
+         * @param ySize The height of the content.
+         */
+        public void onContentDraw(int xOffset, int yOffset, int xSize, int ySize) {
+            synchronized (this) {
+                final boolean firstCall = mLastContentWidth == 0;
+                // The current content buffer is drawn here.
+                mLastContentWidth = xSize;
+                mLastContentHeight = ySize - mLastCaptionHeight;
+                mLastXOffset = xOffset;
+                mLastYOffset = yOffset;
+
+                mRenderer.setContentDrawBounds(
+                        mLastXOffset,
+                        mLastYOffset + mLastCaptionHeight,
+                        mLastXOffset + mLastContentWidth,
+                        mLastYOffset + mLastCaptionHeight + mLastContentHeight);
+                // If this was the first call and changeWindowSizeLocked got already called prior
+                // to us, we should re-issue a changeWindowSizeLocked now.
+                if (firstCall && (mLastCaptionHeight != 0 || !mShowDecor)) {
+                    mOldTargetRect.set(0, 0, 0, 0);
+                    pingRenderLocked();
+                }
+            }
+        }
+
+        /**
+         * Resizing the frame to fit the new window size.
+         * @param newBounds The window bounds which needs to be drawn.
+         */
+        private void changeWindowSizeLocked(Rect newBounds) {
+            // While a configuration change is taking place the view hierarchy might become
+            // inaccessible. For that case we remember the previous metrics to avoid flashes.
+            // Note that even when there is no visible caption, the caption child will exist.
+            View caption = getChildAt(0);
+            if (caption != null) {
+                final int captionHeight = caption.getHeight();
+                // The caption height will probably never dynamically change while we are resizing.
+                // Once set to something other then 0 it should be kept that way.
+                if (captionHeight != 0) {
+                    // Remember the height of the caption.
+                    mLastCaptionHeight = captionHeight;
+                }
+            }
+            // Make sure that the other thread has already prepared the render draw calls for the
+            // content. If any size is 0, we have to wait for it to be drawn first.
+            if ((mLastCaptionHeight == 0 && mShowDecor) ||
+                    mLastContentWidth == 0 || mLastContentHeight == 0) {
+                return;
+            }
+            // Since the surface is spanning the entire screen, we have to add the start offset of
+            // the bounds to get to the surface location.
+            final int left = mLastXOffset + newBounds.left;
+            final int top = mLastYOffset + newBounds.top;
+            final int width = newBounds.width();
+            final int height = newBounds.height();
+
+            // Produce the draw calls.
+            // TODO(skuhne): Create a separate caption view which draws this. If the shadow should
+            // be resized while the window resizes, this hierarchy needs to have the elevation.
+            // That said - it is probably no good idea to draw the shadow every time since it costs
+            // a considerable time which we should rather spend for resizing the content and it does
+            // barely show while the entire screen is moving.
+            mFrameNode.setLeftTopRightBottom(left, top, left + width, top + mLastCaptionHeight);
+            DisplayListCanvas canvas = mFrameNode.start(width, height);
+            canvas.drawColor(Color.BLACK);
+            mFrameNode.end(canvas);
+
+            mBackdropNode.setLeftTopRightBottom(left, top + mLastCaptionHeight, left + width,
+                    top + height);
+
+            // The backdrop: clear everything with the background. Clipping is done elsewhere.
+            canvas = mBackdropNode.start(width, height - mLastCaptionHeight);
+            // TODO(skuhne): mOwner.getDecorView().mBackgroundFallback.draw(..) - or similar.
+            // Note: This might not work (calculator for example uses a transparent background).
+            canvas.drawColor(0xff808080);
+            mBackdropNode.end(canvas);
+
+            // We need to render both rendered nodes explicitly.
+            mRenderer.drawRenderNode(mFrameNode);
+            mRenderer.drawRenderNode(mBackdropNode);
+        }
+
+        /**
+         * Sends a message to the renderer to wake up and perform the next action which can be
+         * either the next rendering or the self destruction if mRenderer is null.
+         * Note: This call must be synchronized.
+         */
+        private void pingRenderLocked() {
+            if (mChoreographer != null) {
+                mChoreographer.postFrameCallback(this);
+            }
+        }
+    }
 }
diff --git a/core/java/com/android/internal/widget/ResolverDrawerLayout.java b/core/java/com/android/internal/widget/ResolverDrawerLayout.java
index 7679624..c4347f8 100644
--- a/core/java/com/android/internal/widget/ResolverDrawerLayout.java
+++ b/core/java/com/android/internal/widget/ResolverDrawerLayout.java
@@ -69,6 +69,12 @@
     private int mCollapsibleHeight;
     private int mUncollapsibleHeight;
 
+    /**
+     * The height in pixels of reserved space added to the top of the collapsed UI;
+     * e.g. chooser targets
+     */
+    private int mCollapsibleHeightReserved;
+
     private int mTopOffset;
 
     private boolean mIsDragging;
@@ -153,12 +159,62 @@
         }
     }
 
+    public void setCollapsibleHeightReserved(int heightPixels) {
+        final int oldReserved = mCollapsibleHeightReserved;
+        mCollapsibleHeightReserved = heightPixels;
+
+        final int dReserved = mCollapsibleHeightReserved - oldReserved;
+        if (dReserved != 0 && mIsDragging) {
+            mLastTouchY -= dReserved;
+        }
+
+        final int oldCollapsibleHeight = mCollapsibleHeight;
+        mCollapsibleHeight = Math.max(mCollapsibleHeight, getMaxCollapsedHeight());
+
+        if (updateCollapseOffset(oldCollapsibleHeight, !isDragging())) {
+            return;
+        }
+
+        invalidate();
+    }
+
     private boolean isMoving() {
         return mIsDragging || !mScroller.isFinished();
     }
 
+    private boolean isDragging() {
+        return mIsDragging || getNestedScrollAxes() == SCROLL_AXIS_VERTICAL;
+    }
+
+    private boolean updateCollapseOffset(int oldCollapsibleHeight, boolean remainClosed) {
+        if (oldCollapsibleHeight == mCollapsibleHeight) {
+            return false;
+        }
+
+        if (isLaidOut()) {
+            final boolean isCollapsedOld = mCollapseOffset != 0;
+            if (remainClosed && (oldCollapsibleHeight < mCollapsibleHeight
+                    && mCollapseOffset == oldCollapsibleHeight)) {
+                // Stay closed even at the new height.
+                mCollapseOffset = mCollapsibleHeight;
+            } else {
+                mCollapseOffset = Math.min(mCollapseOffset, mCollapsibleHeight);
+            }
+            final boolean isCollapsedNew = mCollapseOffset != 0;
+            if (isCollapsedOld != isCollapsedNew) {
+                notifyViewAccessibilityStateChangedIfNeeded(
+                        AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
+            }
+        } else {
+            // Start out collapsed at first unless we restored state for otherwise
+            mCollapseOffset = mOpenOnLayout ? 0 : mCollapsibleHeight;
+        }
+        return true;
+    }
+
     private int getMaxCollapsedHeight() {
-        return isSmallCollapsed() ? mMaxCollapsedHeightSmall : mMaxCollapsedHeight;
+        return (isSmallCollapsed() ? mMaxCollapsedHeightSmall : mMaxCollapsedHeight)
+                + mCollapsibleHeightReserved;
     }
 
     public void setOnDismissedListener(OnDismissedListener listener) {
@@ -676,7 +732,7 @@
             final LayoutParams lp = (LayoutParams) child.getLayoutParams();
             if (lp.alwaysShow && child.getVisibility() != GONE) {
                 measureChildWithMargins(child, widthSpec, widthPadding, heightSpec, heightUsed);
-                heightUsed += lp.topMargin + child.getMeasuredHeight() + lp.bottomMargin;
+                heightUsed += getHeightUsed(child);
             }
         }
 
@@ -688,7 +744,7 @@
             final LayoutParams lp = (LayoutParams) child.getLayoutParams();
             if (!lp.alwaysShow && child.getVisibility() != GONE) {
                 measureChildWithMargins(child, widthSpec, widthPadding, heightSpec, heightUsed);
-                heightUsed += lp.topMargin + child.getMeasuredHeight() + lp.bottomMargin;
+                heightUsed += getHeightUsed(child);
             }
         }
 
@@ -697,30 +753,43 @@
                 heightUsed - alwaysShowHeight - getMaxCollapsedHeight());
         mUncollapsibleHeight = heightUsed - mCollapsibleHeight;
 
-        if (isLaidOut()) {
-            final boolean isCollapsedOld = mCollapseOffset != 0;
-            if (oldCollapsibleHeight < mCollapsibleHeight
-                    && mCollapseOffset == oldCollapsibleHeight) {
-                // Stay closed even at the new height.
-                mCollapseOffset = mCollapsibleHeight;
-            } else {
-                mCollapseOffset = Math.min(mCollapseOffset, mCollapsibleHeight);
-            }
-            final boolean isCollapsedNew = mCollapseOffset != 0;
-            if (isCollapsedOld != isCollapsedNew) {
-                notifyViewAccessibilityStateChangedIfNeeded(
-                        AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
-            }
-        } else {
-            // Start out collapsed at first unless we restored state for otherwise
-            mCollapseOffset = mOpenOnLayout ? 0 : mCollapsibleHeight;
-        }
+        updateCollapseOffset(oldCollapsibleHeight, !isDragging());
 
         mTopOffset = Math.max(0, heightSize - heightUsed) + (int) mCollapseOffset;
 
         setMeasuredDimension(sourceWidth, heightSize);
     }
 
+    private int getHeightUsed(View child) {
+        // This method exists because we're taking a fast path at measuring ListViews that
+        // lets us get away with not doing the more expensive wrap_content measurement which
+        // imposes double child view measurement costs. If we're looking at a ListView, we can
+        // check against the lowest child view plus padding and margin instead of the actual
+        // measured height of the ListView. This lets the ListView hang off the edge when
+        // all of the content would fit on-screen.
+
+        int heightUsed = child.getMeasuredHeight();
+        if (child instanceof AbsListView) {
+            final AbsListView lv = (AbsListView) child;
+            final int lvPaddingBottom = lv.getPaddingBottom();
+
+            int lowest = 0;
+            for (int i = 0, N = lv.getChildCount(); i < N; i++) {
+                final int bottom = lv.getChildAt(i).getBottom() + lvPaddingBottom;
+                if (bottom > lowest) {
+                    lowest = bottom;
+                }
+            }
+
+            if (lowest < heightUsed) {
+                heightUsed = lowest;
+            }
+        }
+
+        final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+        return lp.topMargin + heightUsed + lp.bottomMargin;
+    }
+
     @Override
     protected void onLayout(boolean changed, int l, int t, int r, int b) {
         final int width = getWidth();
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index 223dae0..314e4b6 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -66,10 +66,6 @@
 static jclass   gFontMetricsInt_class;
 static JMetricsID gFontMetricsInt_fieldID;
 
-static jclass   gPaint_class;
-static jfieldID gPaint_nativeInstanceID;
-static jfieldID gPaint_nativeTypefaceID;
-
 static void defaultSettingsForAndroid(Paint* paint) {
     // GlyphID encoding is required because we are using Harfbuzz shaping
     paint->setTextEncoding(Paint::kGlyphID_TextEncoding);
@@ -82,37 +78,17 @@
 
 static thread_local LocaleCacheEntry sSingleEntryLocaleCache;
 
-class PaintGlue {
-public:
+namespace PaintGlue {
     enum MoveOpt {
         AFTER, AT_OR_AFTER, BEFORE, AT_OR_BEFORE, AT
     };
 
-    static Paint* getNativePaint(JNIEnv* env, jobject paint) {
-        SkASSERT(env);
-        SkASSERT(paint);
-        SkASSERT(env->IsInstanceOf(paint, gPaint_class));
-        jlong paintHandle = env->GetLongField(paint, gPaint_nativeInstanceID);
-        android::Paint* p = reinterpret_cast<android::Paint*>(paintHandle);
-        SkASSERT(p);
-        return p;
-    }
-
-    static TypefaceImpl* getNativeTypeface(JNIEnv* env, jobject paint) {
-        SkASSERT(env);
-        SkASSERT(paint);
-        SkASSERT(env->IsInstanceOf(paint, gPaint_class));
-        jlong typefaceHandle = env->GetLongField(paint, gPaint_nativeTypefaceID);
-        android::TypefaceImpl* p = reinterpret_cast<android::TypefaceImpl*>(typefaceHandle);
-        return p;
-    }
-
     static void finalizer(JNIEnv* env, jobject clazz, jlong objHandle) {
         Paint* obj = reinterpret_cast<Paint*>(objHandle);
         delete obj;
     }
 
-    static jlong init(JNIEnv* env, jobject clazz) {
+    static jlong init(JNIEnv* env, jobject) {
         static_assert(1 <<  0 == SkPaint::kAntiAlias_Flag,          "paint_flags_mismatch");
         static_assert(1 <<  2 == SkPaint::kDither_Flag,             "paint_flags_mismatch");
         static_assert(1 <<  3 == SkPaint::kUnderlineText_Flag,      "paint_flags_mismatch");
@@ -149,9 +125,8 @@
     // Equivalent to the Java Paint's FILTER_BITMAP_FLAG.
     static const uint32_t sFilterBitmapFlag = 0x02;
 
-    static jint getFlags(JNIEnv* env, jobject paint) {
-        NPE_CHECK_RETURN_ZERO(env, paint);
-        Paint* nativePaint = getNativePaint(env, paint);
+    static jint getFlags(JNIEnv* env, jobject, jlong paintHandle) {
+        Paint* nativePaint = reinterpret_cast<Paint*>(paintHandle);
         uint32_t result = nativePaint->getFlags();
         result &= ~sFilterBitmapFlag; // Filtering no longer stored in this bit. Mask away.
         if (nativePaint->getFilterQuality() != kNone_SkFilterQuality) {
@@ -160,9 +135,8 @@
         return static_cast<jint>(result);
     }
 
-    static void setFlags(JNIEnv* env, jobject paint, jint flags) {
-        NPE_CHECK_RETURN_VOID(env, paint);
-        Paint* nativePaint = getNativePaint(env, paint);
+    static void setFlags(JNIEnv* env, jobject, jlong paintHandle, jint flags) {
+        Paint* nativePaint = reinterpret_cast<Paint*>(paintHandle);
         // Instead of modifying 0x02, change the filter level.
         nativePaint->setFilterQuality(flags & sFilterBitmapFlag
                 ? kLow_SkFilterQuality
@@ -175,57 +149,47 @@
         nativePaint->setFlags(flags);
     }
 
-    static jint getHinting(JNIEnv* env, jobject paint) {
-        NPE_CHECK_RETURN_ZERO(env, paint);
-        return getNativePaint(env, paint)->getHinting()
+    static jint getHinting(JNIEnv* env, jobject, jlong paintHandle) {
+        return reinterpret_cast<Paint*>(paintHandle)->getHinting()
                 == Paint::kNo_Hinting ? 0 : 1;
     }
 
-    static void setHinting(JNIEnv* env, jobject paint, jint mode) {
-        NPE_CHECK_RETURN_VOID(env, paint);
-        getNativePaint(env, paint)->setHinting(
+    static void setHinting(JNIEnv* env, jobject, jlong paintHandle, jint mode) {
+        reinterpret_cast<Paint*>(paintHandle)->setHinting(
                 mode == 0 ? Paint::kNo_Hinting : Paint::kNormal_Hinting);
     }
 
-    static void setAntiAlias(JNIEnv* env, jobject paint, jboolean aa) {
-        NPE_CHECK_RETURN_VOID(env, paint);
-        getNativePaint(env, paint)->setAntiAlias(aa);
+    static void setAntiAlias(JNIEnv* env, jobject, jlong paintHandle, jboolean aa) {
+        reinterpret_cast<Paint*>(paintHandle)->setAntiAlias(aa);
     }
 
-    static void setLinearText(JNIEnv* env, jobject paint, jboolean linearText) {
-        NPE_CHECK_RETURN_VOID(env, paint);
-        getNativePaint(env, paint)->setLinearText(linearText);
+    static void setLinearText(JNIEnv* env, jobject, jlong paintHandle, jboolean linearText) {
+        reinterpret_cast<Paint*>(paintHandle)->setLinearText(linearText);
     }
 
-    static void setSubpixelText(JNIEnv* env, jobject paint, jboolean subpixelText) {
-        NPE_CHECK_RETURN_VOID(env, paint);
-        getNativePaint(env, paint)->setSubpixelText(subpixelText);
+    static void setSubpixelText(JNIEnv* env, jobject, jlong paintHandle, jboolean subpixelText) {
+        reinterpret_cast<Paint*>(paintHandle)->setSubpixelText(subpixelText);
     }
 
-    static void setUnderlineText(JNIEnv* env, jobject paint, jboolean underlineText) {
-        NPE_CHECK_RETURN_VOID(env, paint);
-        getNativePaint(env, paint)->setUnderlineText(underlineText);
+    static void setUnderlineText(JNIEnv* env, jobject, jlong paintHandle, jboolean underlineText) {
+        reinterpret_cast<Paint*>(paintHandle)->setUnderlineText(underlineText);
     }
 
-    static void setStrikeThruText(JNIEnv* env, jobject paint, jboolean strikeThruText) {
-        NPE_CHECK_RETURN_VOID(env, paint);
-        getNativePaint(env, paint)->setStrikeThruText(strikeThruText);
+    static void setStrikeThruText(JNIEnv* env, jobject, jlong paintHandle, jboolean strikeThruText) {
+        reinterpret_cast<Paint*>(paintHandle)->setStrikeThruText(strikeThruText);
     }
 
-    static void setFakeBoldText(JNIEnv* env, jobject paint, jboolean fakeBoldText) {
-        NPE_CHECK_RETURN_VOID(env, paint);
-        getNativePaint(env, paint)->setFakeBoldText(fakeBoldText);
+    static void setFakeBoldText(JNIEnv* env, jobject, jlong paintHandle, jboolean fakeBoldText) {
+        reinterpret_cast<Paint*>(paintHandle)->setFakeBoldText(fakeBoldText);
     }
 
-    static void setFilterBitmap(JNIEnv* env, jobject paint, jboolean filterBitmap) {
-        NPE_CHECK_RETURN_VOID(env, paint);
-        getNativePaint(env, paint)->setFilterQuality(
+    static void setFilterBitmap(JNIEnv* env, jobject, jlong paintHandle, jboolean filterBitmap) {
+        reinterpret_cast<Paint*>(paintHandle)->setFilterQuality(
                 filterBitmap ? kLow_SkFilterQuality : kNone_SkFilterQuality);
     }
 
-    static void setDither(JNIEnv* env, jobject paint, jboolean dither) {
-        NPE_CHECK_RETURN_VOID(env, paint);
-        getNativePaint(env, paint)->setDither(dither);
+    static void setDither(JNIEnv* env, jobject, jlong paintHandle, jboolean dither) {
+        reinterpret_cast<Paint*>(paintHandle)->setDither(dither);
     }
 
     static jint getStyle(JNIEnv* env, jobject clazz,jlong objHandle) {
@@ -239,48 +203,40 @@
         obj->setStyle(style);
     }
 
-    static jint getColor(JNIEnv* env, jobject paint) {
-        NPE_CHECK_RETURN_ZERO(env, paint);
+    static jint getColor(JNIEnv* env, jobject, jlong paintHandle) {
         int color;
-        color = getNativePaint(env, paint)->getColor();
+        color = reinterpret_cast<Paint*>(paintHandle)->getColor();
         return static_cast<jint>(color);
     }
 
-    static jint getAlpha(JNIEnv* env, jobject paint) {
-        NPE_CHECK_RETURN_ZERO(env, paint);
+    static jint getAlpha(JNIEnv* env, jobject, jlong paintHandle) {
         int alpha;
-        alpha = getNativePaint(env, paint)->getAlpha();
+        alpha = reinterpret_cast<Paint*>(paintHandle)->getAlpha();
         return static_cast<jint>(alpha);
     }
 
-    static void setColor(JNIEnv* env, jobject paint, jint color) {
-        NPE_CHECK_RETURN_VOID(env, paint);
-        getNativePaint(env, paint)->setColor(color);
+    static void setColor(JNIEnv* env, jobject, jlong paintHandle, jint color) {
+        reinterpret_cast<Paint*>(paintHandle)->setColor(color);
     }
 
-    static void setAlpha(JNIEnv* env, jobject paint, jint a) {
-        NPE_CHECK_RETURN_VOID(env, paint);
-        getNativePaint(env, paint)->setAlpha(a);
+    static void setAlpha(JNIEnv* env, jobject, jlong paintHandle, jint a) {
+        reinterpret_cast<Paint*>(paintHandle)->setAlpha(a);
     }
 
-    static jfloat getStrokeWidth(JNIEnv* env, jobject paint) {
-        NPE_CHECK_RETURN_ZERO(env, paint);
-        return SkScalarToFloat(getNativePaint(env, paint)->getStrokeWidth());
+    static jfloat getStrokeWidth(JNIEnv* env, jobject, jlong paintHandle) {
+        return SkScalarToFloat(reinterpret_cast<Paint*>(paintHandle)->getStrokeWidth());
     }
 
-    static void setStrokeWidth(JNIEnv* env, jobject paint, jfloat width) {
-        NPE_CHECK_RETURN_VOID(env, paint);
-        getNativePaint(env, paint)->setStrokeWidth(width);
+    static void setStrokeWidth(JNIEnv* env, jobject, jlong paintHandle, jfloat width) {
+        reinterpret_cast<Paint*>(paintHandle)->setStrokeWidth(width);
     }
 
-    static jfloat getStrokeMiter(JNIEnv* env, jobject paint) {
-        NPE_CHECK_RETURN_ZERO(env, paint);
-        return SkScalarToFloat(getNativePaint(env, paint)->getStrokeMiter());
+    static jfloat getStrokeMiter(JNIEnv* env, jobject, jlong paintHandle) {
+        return SkScalarToFloat(reinterpret_cast<Paint*>(paintHandle)->getStrokeMiter());
     }
 
-    static void setStrokeMiter(JNIEnv* env, jobject paint, jfloat miter) {
-        NPE_CHECK_RETURN_VOID(env, paint);
-        getNativePaint(env, paint)->setStrokeMiter(miter);
+    static void setStrokeMiter(JNIEnv* env, jobject, jlong paintHandle, jfloat miter) {
+        reinterpret_cast<Paint*>(paintHandle)->setStrokeMiter(miter);
     }
 
     static jint getStrokeCap(JNIEnv* env, jobject clazz, jlong objHandle) {
@@ -417,46 +373,38 @@
         obj->setTextLocale(sSingleEntryLocaleCache.languageTag);
     }
 
-    static jboolean isElegantTextHeight(JNIEnv* env, jobject paint) {
-        NPE_CHECK_RETURN_ZERO(env, paint);
-        Paint* obj = getNativePaint(env, paint);
+    static jboolean isElegantTextHeight(JNIEnv* env, jobject, jlong paintHandle) {
+        Paint* obj = reinterpret_cast<Paint*>(paintHandle);
         return obj->getFontVariant() == VARIANT_ELEGANT;
     }
 
-    static void setElegantTextHeight(JNIEnv* env, jobject paint, jboolean aa) {
-        NPE_CHECK_RETURN_VOID(env, paint);
-        Paint* obj = getNativePaint(env, paint);
+    static void setElegantTextHeight(JNIEnv* env, jobject, jlong paintHandle, jboolean aa) {
+        Paint* obj = reinterpret_cast<Paint*>(paintHandle);
         obj->setFontVariant(aa ? VARIANT_ELEGANT : VARIANT_DEFAULT);
     }
 
-    static jfloat getTextSize(JNIEnv* env, jobject paint) {
-        NPE_CHECK_RETURN_ZERO(env, paint);
-        return SkScalarToFloat(getNativePaint(env, paint)->getTextSize());
+    static jfloat getTextSize(JNIEnv* env, jobject, jlong paintHandle) {
+        return SkScalarToFloat(reinterpret_cast<Paint*>(paintHandle)->getTextSize());
     }
 
-    static void setTextSize(JNIEnv* env, jobject paint, jfloat textSize) {
-        NPE_CHECK_RETURN_VOID(env, paint);
-        getNativePaint(env, paint)->setTextSize(textSize);
+    static void setTextSize(JNIEnv* env, jobject, jlong paintHandle, jfloat textSize) {
+        reinterpret_cast<Paint*>(paintHandle)->setTextSize(textSize);
     }
 
-    static jfloat getTextScaleX(JNIEnv* env, jobject paint) {
-        NPE_CHECK_RETURN_ZERO(env, paint);
-        return SkScalarToFloat(getNativePaint(env, paint)->getTextScaleX());
+    static jfloat getTextScaleX(JNIEnv* env, jobject, jlong paintHandle) {
+        return SkScalarToFloat(reinterpret_cast<Paint*>(paintHandle)->getTextScaleX());
     }
 
-    static void setTextScaleX(JNIEnv* env, jobject paint, jfloat scaleX) {
-        NPE_CHECK_RETURN_VOID(env, paint);
-        getNativePaint(env, paint)->setTextScaleX(scaleX);
+    static void setTextScaleX(JNIEnv* env, jobject, jlong paintHandle, jfloat scaleX) {
+        reinterpret_cast<Paint*>(paintHandle)->setTextScaleX(scaleX);
     }
 
-    static jfloat getTextSkewX(JNIEnv* env, jobject paint) {
-        NPE_CHECK_RETURN_ZERO(env, paint);
-        return SkScalarToFloat(getNativePaint(env, paint)->getTextSkewX());
+    static jfloat getTextSkewX(JNIEnv* env, jobject, jlong paintHandle) {
+        return SkScalarToFloat(reinterpret_cast<Paint*>(paintHandle)->getTextSkewX());
     }
 
-    static void setTextSkewX(JNIEnv* env, jobject paint, jfloat skewX) {
-        NPE_CHECK_RETURN_VOID(env, paint);
-        getNativePaint(env, paint)->setTextSkewX(skewX);
+    static void setTextSkewX(JNIEnv* env, jobject, jlong paintHandle, jfloat skewX) {
+        reinterpret_cast<Paint*>(paintHandle)->setTextSkewX(skewX);
     }
 
     static jfloat getLetterSpacing(JNIEnv* env, jobject clazz, jlong paintHandle) {
@@ -489,14 +437,15 @@
         paint->setHyphenEdit((uint32_t)hyphen);
     }
 
-    static SkScalar getMetricsInternal(JNIEnv* env, jobject jpaint, Paint::FontMetrics *metrics) {
+    static SkScalar getMetricsInternal(jlong paintHandle, jlong typefaceHandle,
+            Paint::FontMetrics *metrics) {
         const int kElegantTop = 2500;
         const int kElegantBottom = -1000;
         const int kElegantAscent = 1900;
         const int kElegantDescent = -500;
         const int kElegantLeading = 0;
-        Paint* paint = getNativePaint(env, jpaint);
-        TypefaceImpl* typeface = getNativeTypeface(env, jpaint);
+        Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+        TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
         typeface = TypefaceImpl_resolveDefault(typeface);
         FakedFont baseFont = typeface->fFontCollection->baseFontFaked(typeface->fStyle);
         float saveSkewX = paint->getTextSkewX();
@@ -520,24 +469,22 @@
         return spacing;
     }
 
-    static jfloat ascent(JNIEnv* env, jobject paint) {
-        NPE_CHECK_RETURN_ZERO(env, paint);
+    static jfloat ascent(JNIEnv* env, jobject, jlong paintHandle, jlong typefaceHandle) {
         Paint::FontMetrics metrics;
-        getMetricsInternal(env, paint, &metrics);
+        getMetricsInternal(paintHandle, typefaceHandle, &metrics);
         return SkScalarToFloat(metrics.fAscent);
     }
 
-    static jfloat descent(JNIEnv* env, jobject paint) {
-        NPE_CHECK_RETURN_ZERO(env, paint);
+    static jfloat descent(JNIEnv* env, jobject, jlong paintHandle, jlong typefaceHandle) {
         Paint::FontMetrics metrics;
-        getMetricsInternal(env, paint, &metrics);
+        getMetricsInternal(paintHandle, typefaceHandle, &metrics);
         return SkScalarToFloat(metrics.fDescent);
     }
 
-    static jfloat getFontMetrics(JNIEnv* env, jobject paint, jobject metricsObj) {
-        NPE_CHECK_RETURN_ZERO(env, paint);
+    static jfloat getFontMetrics(JNIEnv* env, jobject, jlong paintHandle,
+            jlong typefaceHandle, jobject metricsObj) {
         Paint::FontMetrics metrics;
-        SkScalar spacing = getMetricsInternal(env, paint, &metrics);
+        SkScalar spacing = getMetricsInternal(paintHandle, typefaceHandle, &metrics);
 
         if (metricsObj) {
             SkASSERT(env->IsInstanceOf(metricsObj, gFontMetrics_class));
@@ -550,11 +497,11 @@
         return SkScalarToFloat(spacing);
     }
 
-    static jint getFontMetricsInt(JNIEnv* env, jobject paint, jobject metricsObj) {
-        NPE_CHECK_RETURN_ZERO(env, paint);
+    static jint getFontMetricsInt(JNIEnv* env, jobject, jlong paintHandle,
+            jlong typefaceHandle, jobject metricsObj) {
         Paint::FontMetrics metrics;
 
-        getMetricsInternal(env, paint, &metrics);
+        getMetricsInternal(paintHandle, typefaceHandle, &metrics);
         int ascent = SkScalarRoundToInt(metrics.fAscent);
         int descent = SkScalarRoundToInt(metrics.fDescent);
         int leading = SkScalarRoundToInt(metrics.fLeading);
@@ -573,7 +520,6 @@
     static jfloat doTextAdvances(JNIEnv *env, Paint *paint, TypefaceImpl* typeface,
             const jchar *text, jint start, jint count, jint contextCount, jint bidiFlags,
             jfloatArray advances, jint advancesIndex) {
-        NPE_CHECK_RETURN_ZERO(env, paint);
         NPE_CHECK_RETURN_ZERO(env, text);
 
         if ((start | count | contextCount | advancesIndex) < 0 || contextCount < count) {
@@ -841,7 +787,7 @@
 
     static void getStringBounds(JNIEnv* env, jobject, jlong paintHandle, jlong typefaceHandle,
                                 jstring text, jint start, jint end, jint bidiFlags, jobject bounds) {
-        const Paint* paint = reinterpret_cast<Paint*>(paintHandle);;
+        const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
         TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
         const jchar* textArray = env->GetStringChars(text, NULL);
         doTextBounds(env, textArray + start, end - start, bounds, *paint, typeface, bidiFlags);
@@ -961,97 +907,97 @@
         return result;
     }
 
-};
+}; // namespace PaintGlue
 
 static const JNINativeMethod methods[] = {
-    {"finalizer", "(J)V", (void*) PaintGlue::finalizer},
-    {"native_init","()J", (void*) PaintGlue::init},
-    {"native_initWithPaint","(J)J", (void*) PaintGlue::initWithPaint},
+    {"nFinalizer", "(J)V", (void*) PaintGlue::finalizer},
+    {"nInit","()J", (void*) PaintGlue::init},
+    {"nInitWithPaint","(J)J", (void*) PaintGlue::initWithPaint},
 
-    {"native_reset","!(J)V", (void*) PaintGlue::reset},
-    {"native_set","!(JJ)V", (void*) PaintGlue::assign},
-    {"getFlags","!()I", (void*) PaintGlue::getFlags},
-    {"setFlags","!(I)V", (void*) PaintGlue::setFlags},
-    {"getHinting","!()I", (void*) PaintGlue::getHinting},
-    {"setHinting","!(I)V", (void*) PaintGlue::setHinting},
-    {"setAntiAlias","!(Z)V", (void*) PaintGlue::setAntiAlias},
-    {"setSubpixelText","!(Z)V", (void*) PaintGlue::setSubpixelText},
-    {"setLinearText","!(Z)V", (void*) PaintGlue::setLinearText},
-    {"setUnderlineText","!(Z)V", (void*) PaintGlue::setUnderlineText},
-    {"setStrikeThruText","!(Z)V", (void*) PaintGlue::setStrikeThruText},
-    {"setFakeBoldText","!(Z)V", (void*) PaintGlue::setFakeBoldText},
-    {"setFilterBitmap","!(Z)V", (void*) PaintGlue::setFilterBitmap},
-    {"setDither","!(Z)V", (void*) PaintGlue::setDither},
-    {"native_getStyle","!(J)I", (void*) PaintGlue::getStyle},
-    {"native_setStyle","!(JI)V", (void*) PaintGlue::setStyle},
-    {"getColor","!()I", (void*) PaintGlue::getColor},
-    {"setColor","!(I)V", (void*) PaintGlue::setColor},
-    {"getAlpha","!()I", (void*) PaintGlue::getAlpha},
-    {"setAlpha","!(I)V", (void*) PaintGlue::setAlpha},
-    {"getStrokeWidth","!()F", (void*) PaintGlue::getStrokeWidth},
-    {"setStrokeWidth","!(F)V", (void*) PaintGlue::setStrokeWidth},
-    {"getStrokeMiter","!()F", (void*) PaintGlue::getStrokeMiter},
-    {"setStrokeMiter","!(F)V", (void*) PaintGlue::setStrokeMiter},
-    {"native_getStrokeCap","!(J)I", (void*) PaintGlue::getStrokeCap},
-    {"native_setStrokeCap","!(JI)V", (void*) PaintGlue::setStrokeCap},
-    {"native_getStrokeJoin","!(J)I", (void*) PaintGlue::getStrokeJoin},
-    {"native_setStrokeJoin","!(JI)V", (void*) PaintGlue::setStrokeJoin},
-    {"native_getFillPath","!(JJJ)Z", (void*) PaintGlue::getFillPath},
-    {"native_setShader","!(JJ)J", (void*) PaintGlue::setShader},
-    {"native_setColorFilter","!(JJ)J", (void*) PaintGlue::setColorFilter},
-    {"native_setXfermode","!(JJ)J", (void*) PaintGlue::setXfermode},
-    {"native_setPathEffect","!(JJ)J", (void*) PaintGlue::setPathEffect},
-    {"native_setMaskFilter","!(JJ)J", (void*) PaintGlue::setMaskFilter},
-    {"native_setTypeface","!(JJ)J", (void*) PaintGlue::setTypeface},
-    {"native_setRasterizer","!(JJ)J", (void*) PaintGlue::setRasterizer},
-    {"native_getTextAlign","!(J)I", (void*) PaintGlue::getTextAlign},
-    {"native_setTextAlign","!(JI)V", (void*) PaintGlue::setTextAlign},
-    {"native_setTextLocale","!(JLjava/lang/String;)V", (void*) PaintGlue::setTextLocale},
-    {"isElegantTextHeight","!()Z", (void*) PaintGlue::isElegantTextHeight},
-    {"setElegantTextHeight","!(Z)V", (void*) PaintGlue::setElegantTextHeight},
-    {"getTextSize","!()F", (void*) PaintGlue::getTextSize},
-    {"setTextSize","!(F)V", (void*) PaintGlue::setTextSize},
-    {"getTextScaleX","!()F", (void*) PaintGlue::getTextScaleX},
-    {"setTextScaleX","!(F)V", (void*) PaintGlue::setTextScaleX},
-    {"getTextSkewX","!()F", (void*) PaintGlue::getTextSkewX},
-    {"setTextSkewX","!(F)V", (void*) PaintGlue::setTextSkewX},
-    {"native_getLetterSpacing","!(J)F", (void*) PaintGlue::getLetterSpacing},
-    {"native_setLetterSpacing","!(JF)V", (void*) PaintGlue::setLetterSpacing},
-    {"native_setFontFeatureSettings","(JLjava/lang/String;)V",
+    {"nReset","!(J)V", (void*) PaintGlue::reset},
+    {"nSet","!(JJ)V", (void*) PaintGlue::assign},
+    {"nGetFlags","!(J)I", (void*) PaintGlue::getFlags},
+    {"nSetFlags","!(JI)V", (void*) PaintGlue::setFlags},
+    {"nGetHinting","!(J)I", (void*) PaintGlue::getHinting},
+    {"nSetHinting","!(JI)V", (void*) PaintGlue::setHinting},
+    {"nSetAntiAlias","!(JZ)V", (void*) PaintGlue::setAntiAlias},
+    {"nSetSubpixelText","!(JZ)V", (void*) PaintGlue::setSubpixelText},
+    {"nSetLinearText","!(JZ)V", (void*) PaintGlue::setLinearText},
+    {"nSetUnderlineText","!(JZ)V", (void*) PaintGlue::setUnderlineText},
+    {"nSetStrikeThruText","!(JZ)V", (void*) PaintGlue::setStrikeThruText},
+    {"nSetFakeBoldText","!(JZ)V", (void*) PaintGlue::setFakeBoldText},
+    {"nSetFilterBitmap","!(JZ)V", (void*) PaintGlue::setFilterBitmap},
+    {"nSetDither","!(JZ)V", (void*) PaintGlue::setDither},
+    {"nGetStyle","!(J)I", (void*) PaintGlue::getStyle},
+    {"nSetStyle","!(JI)V", (void*) PaintGlue::setStyle},
+    {"nGetColor","!(J)I", (void*) PaintGlue::getColor},
+    {"nSetColor","!(JI)V", (void*) PaintGlue::setColor},
+    {"nGetAlpha","!(J)I", (void*) PaintGlue::getAlpha},
+    {"nSetAlpha","!(JI)V", (void*) PaintGlue::setAlpha},
+    {"nGetStrokeWidth","!(J)F", (void*) PaintGlue::getStrokeWidth},
+    {"nSetStrokeWidth","!(JF)V", (void*) PaintGlue::setStrokeWidth},
+    {"nGetStrokeMiter","!(J)F", (void*) PaintGlue::getStrokeMiter},
+    {"nSetStrokeMiter","!(JF)V", (void*) PaintGlue::setStrokeMiter},
+    {"nGetStrokeCap","!(J)I", (void*) PaintGlue::getStrokeCap},
+    {"nSetStrokeCap","!(JI)V", (void*) PaintGlue::setStrokeCap},
+    {"nGetStrokeJoin","!(J)I", (void*) PaintGlue::getStrokeJoin},
+    {"nSetStrokeJoin","!(JI)V", (void*) PaintGlue::setStrokeJoin},
+    {"nGetFillPath","!(JJJ)Z", (void*) PaintGlue::getFillPath},
+    {"nSetShader","!(JJ)J", (void*) PaintGlue::setShader},
+    {"nSetColorFilter","!(JJ)J", (void*) PaintGlue::setColorFilter},
+    {"nSetXfermode","!(JJ)J", (void*) PaintGlue::setXfermode},
+    {"nSetPathEffect","!(JJ)J", (void*) PaintGlue::setPathEffect},
+    {"nSetMaskFilter","!(JJ)J", (void*) PaintGlue::setMaskFilter},
+    {"nSetTypeface","!(JJ)J", (void*) PaintGlue::setTypeface},
+    {"nSetRasterizer","!(JJ)J", (void*) PaintGlue::setRasterizer},
+    {"nGetTextAlign","!(J)I", (void*) PaintGlue::getTextAlign},
+    {"nSetTextAlign","!(JI)V", (void*) PaintGlue::setTextAlign},
+    {"nSetTextLocale","!(JLjava/lang/String;)V", (void*) PaintGlue::setTextLocale},
+    {"nIsElegantTextHeight","!(J)Z", (void*) PaintGlue::isElegantTextHeight},
+    {"nSetElegantTextHeight","!(JZ)V", (void*) PaintGlue::setElegantTextHeight},
+    {"nGetTextSize","!(J)F", (void*) PaintGlue::getTextSize},
+    {"nSetTextSize","!(JF)V", (void*) PaintGlue::setTextSize},
+    {"nGetTextScaleX","!(J)F", (void*) PaintGlue::getTextScaleX},
+    {"nSetTextScaleX","!(JF)V", (void*) PaintGlue::setTextScaleX},
+    {"nGetTextSkewX","!(J)F", (void*) PaintGlue::getTextSkewX},
+    {"nSetTextSkewX","!(JF)V", (void*) PaintGlue::setTextSkewX},
+    {"nGetLetterSpacing","!(J)F", (void*) PaintGlue::getLetterSpacing},
+    {"nSetLetterSpacing","!(JF)V", (void*) PaintGlue::setLetterSpacing},
+    {"nSetFontFeatureSettings","(JLjava/lang/String;)V",
             (void*) PaintGlue::setFontFeatureSettings},
-    {"native_getHyphenEdit", "!(J)I", (void*) PaintGlue::getHyphenEdit},
-    {"native_setHyphenEdit", "!(JI)V", (void*) PaintGlue::setHyphenEdit},
-    {"ascent","!()F", (void*) PaintGlue::ascent},
-    {"descent","!()F", (void*) PaintGlue::descent},
+    {"nGetHyphenEdit", "!(J)I", (void*) PaintGlue::getHyphenEdit},
+    {"nSetHyphenEdit", "!(JI)V", (void*) PaintGlue::setHyphenEdit},
+    {"nAscent","!(JJ)F", (void*) PaintGlue::ascent},
+    {"nDescent","!(JJ)F", (void*) PaintGlue::descent},
 
-    {"getFontMetrics", "!(Landroid/graphics/Paint$FontMetrics;)F",
+    {"nGetFontMetrics", "!(JJLandroid/graphics/Paint$FontMetrics;)F",
             (void*)PaintGlue::getFontMetrics},
-    {"getFontMetricsInt", "!(Landroid/graphics/Paint$FontMetricsInt;)I",
+    {"nGetFontMetricsInt", "!(JJLandroid/graphics/Paint$FontMetricsInt;)I",
             (void*)PaintGlue::getFontMetricsInt},
 
-    {"native_breakText","(JJ[CIIFI[F)I", (void*) PaintGlue::breakTextC},
-    {"native_breakText","(JJLjava/lang/String;ZFI[F)I", (void*) PaintGlue::breakTextS},
-    {"native_getTextAdvances","(JJ[CIIIII[FI)F",
+    {"nBreakText","(JJ[CIIFI[F)I", (void*) PaintGlue::breakTextC},
+    {"nBreakText","(JJLjava/lang/String;ZFI[F)I", (void*) PaintGlue::breakTextS},
+    {"nGetTextAdvances","(JJ[CIIIII[FI)F",
             (void*) PaintGlue::getTextAdvances___CIIIII_FI},
-    {"native_getTextAdvances","(JJLjava/lang/String;IIIII[FI)F",
+    {"nGetTextAdvances","(JJLjava/lang/String;IIIII[FI)F",
             (void*) PaintGlue::getTextAdvances__StringIIIII_FI},
 
-    {"native_getTextRunCursor", "(J[CIIIII)I", (void*) PaintGlue::getTextRunCursor___C},
-    {"native_getTextRunCursor", "(JLjava/lang/String;IIIII)I",
+    {"nGetTextRunCursor", "(J[CIIIII)I", (void*) PaintGlue::getTextRunCursor___C},
+    {"nGetTextRunCursor", "(JLjava/lang/String;IIIII)I",
             (void*) PaintGlue::getTextRunCursor__String},
-    {"native_getTextPath", "(JJI[CIIFFJ)V", (void*) PaintGlue::getTextPath___C},
-    {"native_getTextPath", "(JJILjava/lang/String;IIFFJ)V", (void*) PaintGlue::getTextPath__String},
-    {"nativeGetStringBounds", "(JJLjava/lang/String;IIILandroid/graphics/Rect;)V",
+    {"nGetTextPath", "(JJI[CIIFFJ)V", (void*) PaintGlue::getTextPath___C},
+    {"nGetTextPath", "(JJILjava/lang/String;IIFFJ)V", (void*) PaintGlue::getTextPath__String},
+    {"nGetStringBounds", "(JJLjava/lang/String;IIILandroid/graphics/Rect;)V",
             (void*) PaintGlue::getStringBounds },
-    {"nativeGetCharArrayBounds", "(JJ[CIIILandroid/graphics/Rect;)V",
+    {"nGetCharArrayBounds", "(JJ[CIIILandroid/graphics/Rect;)V",
             (void*) PaintGlue::getCharArrayBounds },
-    {"native_hasGlyph", "(JJILjava/lang/String;)Z", (void*) PaintGlue::hasGlyph },
-    {"native_getRunAdvance", "(JJ[CIIIIZI)F", (void*) PaintGlue::getRunAdvance___CIIIIZI_F},
-    {"native_getOffsetForAdvance", "(JJ[CIIIIZF)I",
+    {"nHasGlyph", "(JJILjava/lang/String;)Z", (void*) PaintGlue::hasGlyph },
+    {"nGetRunAdvance", "(JJ[CIIIIZI)F", (void*) PaintGlue::getRunAdvance___CIIIIZI_F},
+    {"nGetOffsetForAdvance", "(JJ[CIIIIZF)I",
             (void*) PaintGlue::getOffsetForAdvance___CIIIIZF_I},
 
-    {"native_setShadowLayer", "!(JFFFI)V", (void*)PaintGlue::setShadowLayer},
-    {"native_hasShadowLayer", "!(J)Z", (void*)PaintGlue::hasShadowLayer}
+    {"nSetShadowLayer", "!(JFFFI)V", (void*)PaintGlue::setShadowLayer},
+    {"nHasShadowLayer", "!(J)Z", (void*)PaintGlue::hasShadowLayer}
 };
 
 int register_android_graphics_Paint(JNIEnv* env) {
@@ -1073,10 +1019,6 @@
     gFontMetricsInt_fieldID.bottom = GetFieldIDOrDie(env, gFontMetricsInt_class, "bottom", "I");
     gFontMetricsInt_fieldID.leading = GetFieldIDOrDie(env, gFontMetricsInt_class, "leading", "I");
 
-    gPaint_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/Paint"));
-    gPaint_nativeInstanceID = GetFieldIDOrDie(env, gPaint_class, "mNativePaint", "J");
-    gPaint_nativeTypefaceID = GetFieldIDOrDie(env, gPaint_class, "mNativeTypeface", "J");
-
     return RegisterMethodsOrDie(env, "android/graphics/Paint", methods, NELEM(methods));
 }
 
diff --git a/core/jni/android_graphics_Canvas.cpp b/core/jni/android_graphics_Canvas.cpp
index 32a877a..3d96fab 100644
--- a/core/jni/android_graphics_Canvas.cpp
+++ b/core/jni/android_graphics_Canvas.cpp
@@ -749,57 +749,57 @@
 static const JNINativeMethod gMethods[] = {
     {"finalizer", "(J)V", (void*) CanvasJNI::finalizer},
     {"initRaster", "(Landroid/graphics/Bitmap;)J", (void*) CanvasJNI::initRaster},
-    {"native_setBitmap", "(JLandroid/graphics/Bitmap;)V", (void*) CanvasJNI::setBitmap},
-    {"native_isOpaque","(J)Z", (void*) CanvasJNI::isOpaque},
-    {"native_getWidth","(J)I", (void*) CanvasJNI::getWidth},
-    {"native_getHeight","(J)I", (void*) CanvasJNI::getHeight},
-    {"native_setHighContrastText","(JZ)V", (void*) CanvasJNI::setHighContrastText},
-    {"native_save","(JI)I", (void*) CanvasJNI::save},
-    {"native_saveLayer","(JFFFFJI)I", (void*) CanvasJNI::saveLayer},
-    {"native_saveLayerAlpha","(JFFFFII)I", (void*) CanvasJNI::saveLayerAlpha},
-    {"native_getSaveCount","(J)I", (void*) CanvasJNI::getSaveCount},
-    {"native_restore","(JZ)V", (void*) CanvasJNI::restore},
-    {"native_restoreToCount","(JIZ)V", (void*) CanvasJNI::restoreToCount},
-    {"native_getCTM", "(JJ)V", (void*)CanvasJNI::getCTM},
-    {"native_setMatrix","(JJ)V", (void*) CanvasJNI::setMatrix},
-    {"native_concat","(JJ)V", (void*) CanvasJNI::concat},
-    {"native_rotate","(JF)V", (void*) CanvasJNI::rotate},
-    {"native_scale","(JFF)V", (void*) CanvasJNI::scale},
-    {"native_skew","(JFF)V", (void*) CanvasJNI::skew},
-    {"native_translate","(JFF)V", (void*) CanvasJNI::translate},
-    {"native_getClipBounds","(JLandroid/graphics/Rect;)Z", (void*) CanvasJNI::getClipBounds},
-    {"native_quickReject","(JJ)Z", (void*) CanvasJNI::quickRejectPath},
-    {"native_quickReject","(JFFFF)Z", (void*)CanvasJNI::quickRejectRect},
-    {"native_clipRect","(JFFFFI)Z", (void*) CanvasJNI::clipRect},
-    {"native_clipPath","(JJI)Z", (void*) CanvasJNI::clipPath},
-    {"native_clipRegion","(JJI)Z", (void*) CanvasJNI::clipRegion},
-    {"native_drawColor","(JII)V", (void*) CanvasJNI::drawColor},
-    {"native_drawPaint","(JJ)V", (void*) CanvasJNI::drawPaint},
-    {"native_drawPoint", "(JFFJ)V", (void*) CanvasJNI::drawPoint},
-    {"native_drawPoints", "(J[FIIJ)V", (void*) CanvasJNI::drawPoints},
-    {"native_drawLine", "(JFFFFJ)V", (void*) CanvasJNI::drawLine},
-    {"native_drawLines", "(J[FIIJ)V", (void*) CanvasJNI::drawLines},
-    {"native_drawRect","(JFFFFJ)V", (void*) CanvasJNI::drawRect},
-    {"native_drawRegion", "(JJJ)V", (void*) CanvasJNI::drawRegion },
-    {"native_drawRoundRect","(JFFFFFFJ)V", (void*) CanvasJNI::drawRoundRect},
-    {"native_drawCircle","(JFFFJ)V", (void*) CanvasJNI::drawCircle},
-    {"native_drawOval","(JFFFFJ)V", (void*) CanvasJNI::drawOval},
-    {"native_drawArc","(JFFFFFFZJ)V", (void*) CanvasJNI::drawArc},
-    {"native_drawPath","(JJJ)V", (void*) CanvasJNI::drawPath},
-    {"nativeDrawVertices", "(JII[FI[FI[II[SIIJ)V", (void*)CanvasJNI::drawVertices},
-    {"native_drawNinePatch", "(JJJFFFFJII)V", (void*)CanvasJNI::drawNinePatch},
-    {"native_drawBitmap","(JLandroid/graphics/Bitmap;FFJIII)V", (void*) CanvasJNI::drawBitmap},
-    {"nativeDrawBitmapMatrix", "(JLandroid/graphics/Bitmap;JJ)V", (void*)CanvasJNI::drawBitmapMatrix},
-    {"native_drawBitmap","(JLandroid/graphics/Bitmap;FFFFFFFFJII)V", (void*) CanvasJNI::drawBitmapRect},
-    {"native_drawBitmap", "(J[IIIFFIIZJ)V", (void*)CanvasJNI::drawBitmapArray},
-    {"nativeDrawBitmapMesh", "(JLandroid/graphics/Bitmap;II[FI[IIJ)V", (void*)CanvasJNI::drawBitmapMesh},
-    {"native_drawText","(J[CIIFFIJJ)V", (void*) CanvasJNI::drawTextChars},
-    {"native_drawText","(JLjava/lang/String;IIFFIJJ)V", (void*) CanvasJNI::drawTextString},
-    {"native_drawTextRun","(J[CIIIIFFZJJ)V", (void*) CanvasJNI::drawTextRunChars},
-    {"native_drawTextRun","(JLjava/lang/String;IIIIFFZJJ)V", (void*) CanvasJNI::drawTextRunString},
-    {"native_drawTextOnPath","(J[CIIJFFIJJ)V", (void*) CanvasJNI::drawTextOnPathChars},
-    {"native_drawTextOnPath","(JLjava/lang/String;JFFIJJ)V", (void*) CanvasJNI::drawTextOnPathString},
-    {"nativeSetDrawFilter", "(JJ)V", (void*) CanvasJNI::setDrawFilter},
+    {"native_setBitmap", "!(JLandroid/graphics/Bitmap;)V", (void*) CanvasJNI::setBitmap},
+    {"native_isOpaque","!(J)Z", (void*) CanvasJNI::isOpaque},
+    {"native_getWidth","!(J)I", (void*) CanvasJNI::getWidth},
+    {"native_getHeight","!(J)I", (void*) CanvasJNI::getHeight},
+    {"native_setHighContrastText","!(JZ)V", (void*) CanvasJNI::setHighContrastText},
+    {"native_save","!(JI)I", (void*) CanvasJNI::save},
+    {"native_saveLayer","!(JFFFFJI)I", (void*) CanvasJNI::saveLayer},
+    {"native_saveLayerAlpha","!(JFFFFII)I", (void*) CanvasJNI::saveLayerAlpha},
+    {"native_getSaveCount","!(J)I", (void*) CanvasJNI::getSaveCount},
+    {"native_restore","!(JZ)V", (void*) CanvasJNI::restore},
+    {"native_restoreToCount","!(JIZ)V", (void*) CanvasJNI::restoreToCount},
+    {"native_getCTM", "!(JJ)V", (void*)CanvasJNI::getCTM},
+    {"native_setMatrix","!(JJ)V", (void*) CanvasJNI::setMatrix},
+    {"native_concat","!(JJ)V", (void*) CanvasJNI::concat},
+    {"native_rotate","!(JF)V", (void*) CanvasJNI::rotate},
+    {"native_scale","!(JFF)V", (void*) CanvasJNI::scale},
+    {"native_skew","!(JFF)V", (void*) CanvasJNI::skew},
+    {"native_translate","!(JFF)V", (void*) CanvasJNI::translate},
+    {"native_getClipBounds","!(JLandroid/graphics/Rect;)Z", (void*) CanvasJNI::getClipBounds},
+    {"native_quickReject","!(JJ)Z", (void*) CanvasJNI::quickRejectPath},
+    {"native_quickReject","!(JFFFF)Z", (void*)CanvasJNI::quickRejectRect},
+    {"native_clipRect","!(JFFFFI)Z", (void*) CanvasJNI::clipRect},
+    {"native_clipPath","!(JJI)Z", (void*) CanvasJNI::clipPath},
+    {"native_clipRegion","!(JJI)Z", (void*) CanvasJNI::clipRegion},
+    {"native_drawColor","!(JII)V", (void*) CanvasJNI::drawColor},
+    {"native_drawPaint","!(JJ)V", (void*) CanvasJNI::drawPaint},
+    {"native_drawPoint", "!(JFFJ)V", (void*) CanvasJNI::drawPoint},
+    {"native_drawPoints", "!(J[FIIJ)V", (void*) CanvasJNI::drawPoints},
+    {"native_drawLine", "!(JFFFFJ)V", (void*) CanvasJNI::drawLine},
+    {"native_drawLines", "!(J[FIIJ)V", (void*) CanvasJNI::drawLines},
+    {"native_drawRect","!(JFFFFJ)V", (void*) CanvasJNI::drawRect},
+    {"native_drawRegion", "!(JJJ)V", (void*) CanvasJNI::drawRegion },
+    {"native_drawRoundRect","!(JFFFFFFJ)V", (void*) CanvasJNI::drawRoundRect},
+    {"native_drawCircle","!(JFFFJ)V", (void*) CanvasJNI::drawCircle},
+    {"native_drawOval","!(JFFFFJ)V", (void*) CanvasJNI::drawOval},
+    {"native_drawArc","!(JFFFFFFZJ)V", (void*) CanvasJNI::drawArc},
+    {"native_drawPath","!(JJJ)V", (void*) CanvasJNI::drawPath},
+    {"nativeDrawVertices", "!(JII[FI[FI[II[SIIJ)V", (void*)CanvasJNI::drawVertices},
+    {"native_drawNinePatch", "!(JJJFFFFJII)V", (void*)CanvasJNI::drawNinePatch},
+    {"native_drawBitmap","!(JLandroid/graphics/Bitmap;FFJIII)V", (void*) CanvasJNI::drawBitmap},
+    {"nativeDrawBitmapMatrix", "!(JLandroid/graphics/Bitmap;JJ)V", (void*)CanvasJNI::drawBitmapMatrix},
+    {"native_drawBitmap","!(JLandroid/graphics/Bitmap;FFFFFFFFJII)V", (void*) CanvasJNI::drawBitmapRect},
+    {"native_drawBitmap", "!(J[IIIFFIIZJ)V", (void*)CanvasJNI::drawBitmapArray},
+    {"nativeDrawBitmapMesh", "!(JLandroid/graphics/Bitmap;II[FI[IIJ)V", (void*)CanvasJNI::drawBitmapMesh},
+    {"native_drawText","!(J[CIIFFIJJ)V", (void*) CanvasJNI::drawTextChars},
+    {"native_drawText","!(JLjava/lang/String;IIFFIJJ)V", (void*) CanvasJNI::drawTextString},
+    {"native_drawTextRun","!(J[CIIIIFFZJJ)V", (void*) CanvasJNI::drawTextRunChars},
+    {"native_drawTextRun","!(JLjava/lang/String;IIIIFFZJJ)V", (void*) CanvasJNI::drawTextRunString},
+    {"native_drawTextOnPath","!(J[CIIJFFIJJ)V", (void*) CanvasJNI::drawTextOnPathChars},
+    {"native_drawTextOnPath","!(JLjava/lang/String;JFFIJJ)V", (void*) CanvasJNI::drawTextOnPathString},
+    {"nativeSetDrawFilter", "!(JJ)V", (void*) CanvasJNI::setDrawFilter},
     {"freeCaches", "()V", (void*) CanvasJNI::freeCaches},
     {"freeTextLayoutCaches", "()V", (void*) CanvasJNI::freeTextLayoutCaches}
 };
diff --git a/core/jni/android_view_DisplayEventReceiver.cpp b/core/jni/android_view_DisplayEventReceiver.cpp
index 437bd19..24eb961 100644
--- a/core/jni/android_view_DisplayEventReceiver.cpp
+++ b/core/jni/android_view_DisplayEventReceiver.cpp
@@ -267,7 +267,7 @@
     { "nativeDispose",
             "(J)V",
             (void*)nativeDispose },
-    { "nativeScheduleVsync", "(J)V",
+    { "nativeScheduleVsync", "!(J)V",
             (void*)nativeScheduleVsync }
 };
 
diff --git a/core/jni/android_view_DisplayListCanvas.cpp b/core/jni/android_view_DisplayListCanvas.cpp
index 460c1a1..b64acc3 100644
--- a/core/jni/android_view_DisplayListCanvas.cpp
+++ b/core/jni/android_view_DisplayListCanvas.cpp
@@ -174,24 +174,24 @@
 const char* const kClassPathName = "android/view/DisplayListCanvas";
 
 static JNINativeMethod gMethods[] = {
-    { "nIsAvailable",       "()Z",             (void*) android_view_DisplayListCanvas_isAvailable },
-    { "nInsertReorderBarrier","(JZ)V",         (void*) android_view_DisplayListCanvas_insertReorderBarrier },
+    { "nIsAvailable",       "!()Z",             (void*) android_view_DisplayListCanvas_isAvailable },
+    { "nInsertReorderBarrier","!(JZ)V",         (void*) android_view_DisplayListCanvas_insertReorderBarrier },
 
-    { "nCallDrawGLFunction", "(JJ)V",          (void*) android_view_DisplayListCanvas_callDrawGLFunction },
+    { "nCallDrawGLFunction", "!(JJ)V",          (void*) android_view_DisplayListCanvas_callDrawGLFunction },
 
-    { "nDrawRoundRect",     "(JJJJJJJJ)V",     (void*) android_view_DisplayListCanvas_drawRoundRectProps },
-    { "nDrawCircle",        "(JJJJJ)V",        (void*) android_view_DisplayListCanvas_drawCircleProps },
+    { "nDrawRoundRect",     "!(JJJJJJJJ)V",     (void*) android_view_DisplayListCanvas_drawRoundRectProps },
+    { "nDrawCircle",        "!(JJJJJ)V",        (void*) android_view_DisplayListCanvas_drawCircleProps },
 
-    { "nFinishRecording",   "(J)J",            (void*) android_view_DisplayListCanvas_finishRecording },
-    { "nDrawRenderNode",    "(JJ)V",           (void*) android_view_DisplayListCanvas_drawRenderNode },
+    { "nFinishRecording",   "!(J)J",            (void*) android_view_DisplayListCanvas_finishRecording },
+    { "nDrawRenderNode",    "!(JJ)V",           (void*) android_view_DisplayListCanvas_drawRenderNode },
 
-    { "nCreateDisplayListCanvas", "(II)J",     (void*) android_view_DisplayListCanvas_createDisplayListCanvas },
-    { "nResetDisplayListCanvas", "(JII)V",     (void*) android_view_DisplayListCanvas_resetDisplayListCanvas },
+    { "nCreateDisplayListCanvas", "!(II)J",     (void*) android_view_DisplayListCanvas_createDisplayListCanvas },
+    { "nResetDisplayListCanvas", "!(JII)V",     (void*) android_view_DisplayListCanvas_resetDisplayListCanvas },
 
-    { "nDrawLayer",               "(JJ)V",     (void*) android_view_DisplayListCanvas_drawLayer },
+    { "nDrawLayer",               "!(JJ)V",     (void*) android_view_DisplayListCanvas_drawLayer },
 
-    { "nGetMaximumTextureWidth",  "()I",       (void*) android_view_DisplayListCanvas_getMaxTextureWidth },
-    { "nGetMaximumTextureHeight", "()I",       (void*) android_view_DisplayListCanvas_getMaxTextureHeight },
+    { "nGetMaximumTextureWidth",  "!()I",       (void*) android_view_DisplayListCanvas_getMaxTextureWidth },
+    { "nGetMaximumTextureHeight", "!()I",       (void*) android_view_DisplayListCanvas_getMaxTextureHeight },
 };
 
 static JNINativeMethod gActivityThreadMethods[] = {
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index c79f833..17eb876 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -461,10 +461,10 @@
     proxy->drawRenderNode(renderNode);
 }
 
-static void android_view_ThreadedRenderer_setContentOverdrawProtectionBounds(JNIEnv* env,
+static void android_view_ThreadedRenderer_setContentDrawBounds(JNIEnv* env,
         jobject clazz, jlong proxyPtr, jint left, jint top, jint right, jint bottom) {
     RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
-    proxy->setContentOverdrawProtectionBounds(left, top, right, bottom);
+    proxy->setContentDrawBounds(left, top, right, bottom);
 }
 
 // ----------------------------------------------------------------------------
@@ -522,8 +522,7 @@
     { "nAddRenderNode", "(JJZ)V", (void*) android_view_ThreadedRenderer_addRenderNode},
     { "nRemoveRenderNode", "(JJ)V", (void*) android_view_ThreadedRenderer_removeRenderNode},
     { "nDrawRenderNode", "(JJ)V", (void*) android_view_ThreadedRendererd_drawRenderNode},
-    { "nSetContentOverdrawProtectionBounds", "(JIIII)V",
-                (void*)android_view_ThreadedRenderer_setContentOverdrawProtectionBounds},
+    { "nSetContentDrawBounds", "(JIIII)V", (void*)android_view_ThreadedRenderer_setContentDrawBounds},
 };
 
 int register_android_view_ThreadedRenderer(JNIEnv* env) {
diff --git a/core/res/res/layout/chooser_grid.xml b/core/res/res/layout/chooser_grid.xml
index dcdfb6c..41726fb 100644
--- a/core/res/res/layout/chooser_grid.xml
+++ b/core/res/res/layout/chooser_grid.xml
@@ -85,7 +85,7 @@
 
     <ListView
             android:layout_width="match_parent"
-            android:layout_height="wrap_content"
+            android:layout_height="match_parent"
             android:id="@+id/resolver_list"
             android:clipToPadding="false"
             android:scrollbarStyle="outsideOverlay"
diff --git a/core/res/res/layout/list_content.xml b/core/res/res/layout/list_content.xml
index 1414032..45ade4d 100644
--- a/core/res/res/layout/list_content.xml
+++ b/core/res/res/layout/list_content.xml
@@ -44,8 +44,7 @@
             
         <ListView android:id="@android:id/list"
                 android:layout_width="match_parent" 
-                android:layout_height="match_parent"
-                android:drawSelectorOnTop="false" />
+                android:layout_height="match_parent" />
         <TextView android:id="@+android:id/internalEmpty"
                 android:layout_width="match_parent"
                 android:layout_height="match_parent"
diff --git a/core/res/res/layout/preference_list_fragment.xml b/core/res/res/layout/preference_list_fragment.xml
index 4e895b0..f073c33 100644
--- a/core/res/res/layout/preference_list_fragment.xml
+++ b/core/res/res/layout/preference_list_fragment.xml
@@ -41,6 +41,7 @@
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:padding="@dimen/preference_fragment_padding_side"
+        android:textAppearance="?android:attr/textAppearanceMedium"
         android:gravity="center"
         android:visibility="gone" />
 
diff --git a/core/res/res/layout/resolve_grid_item.xml b/core/res/res/layout/resolve_grid_item.xml
index 1c496f6..0a7ac77 100644
--- a/core/res/res/layout/resolve_grid_item.xml
+++ b/core/res/res/layout/resolve_grid_item.xml
@@ -70,6 +70,7 @@
               android:minLines="2"
               android:maxLines="2"
               android:gravity="top|center_horizontal"
-              android:ellipsize="marquee" />
+              android:ellipsize="marquee"
+              android:visibility="gone" />
 </LinearLayout>
 
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 45d9dc3..5032c25 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -51,7 +51,7 @@
     <string name="serviceDisabled" msgid="1937553226592516411">"سرویس غیرفعال شده است."</string>
     <string name="serviceRegistered" msgid="6275019082598102493">"ثبت با موفقیت انجام شد"</string>
     <string name="serviceErased" msgid="1288584695297200972">"پاک کردن با موفقیت انجام شد."</string>
-    <string name="passwordIncorrect" msgid="7612208839450128715">"رمز ورود اشتباه است."</string>
+    <string name="passwordIncorrect" msgid="7612208839450128715">"گذرواژه اشتباه است."</string>
     <string name="mmiComplete" msgid="8232527495411698359">"‏MMI کامل شد."</string>
     <string name="badPin" msgid="9015277645546710014">"‏پین قدیمی که نوشته‎اید صحیح نیست."</string>
     <string name="badPuk" msgid="5487257647081132201">"‏PUK که نوشته‌اید صحیح نیست."</string>
@@ -74,7 +74,7 @@
     <string name="CfMmi" msgid="5123218989141573515">"هدایت تماس"</string>
     <string name="CwMmi" msgid="9129678056795016867">"انتظار تماس"</string>
     <string name="BaMmi" msgid="455193067926770581">"محدودیت تماس"</string>
-    <string name="PwdMmi" msgid="7043715687905254199">"تغییر رمز ورود"</string>
+    <string name="PwdMmi" msgid="7043715687905254199">"تغییر گذرواژه"</string>
     <string name="PinMmi" msgid="3113117780361190304">"تغییر پین"</string>
     <string name="CnipMmi" msgid="3110534680557857162">"شماره تماس حاضر"</string>
     <string name="CnirMmi" msgid="3062102121430548731">"شماره تماس محدود شده"</string>
@@ -495,7 +495,7 @@
     <string name="permdesc_bindCarrierServices" msgid="1391552602551084192">"به دارنده امکان می‌دهد به سرویس‌های شرکت مخابراتی متصل شود. هرگز نباید برای برنامه‌های عادی مورد نیاز باشد."</string>
     <string name="permlab_access_notification_policy" msgid="4247510821662059671">"دسترسی به حالت «مزاحم نشوید»"</string>
     <string name="permdesc_access_notification_policy" msgid="3296832375218749580">"به برنامه امکان می‌دهد پیکربندی «مزاحم نشوید» را بخواند و بنویسد."</string>
-    <string name="policylab_limitPassword" msgid="4497420728857585791">"تنظیم قوانین رمز ورود"</string>
+    <string name="policylab_limitPassword" msgid="4497420728857585791">"تنظیم قوانین گذرواژه"</string>
     <string name="policydesc_limitPassword" msgid="2502021457917874968">"کنترل طول و نوع نویسه‌هایی که در گذرواژه و پین قفل صفحه مجاز است."</string>
     <string name="policylab_watchLogin" msgid="914130646942199503">"نمایش تلاش‌های قفل گشایی صفحه"</string>
     <string name="policydesc_watchLogin" product="tablet" msgid="3215729294215070072">"‏تعداد گذرواژه‎های نادرست تایپ شده را هنگام بازکردن قفل صفحه کنترل می‌کند، و اگر دفعات زیادی گذرواژه نادرست وارد شود رایانهٔ لوحی را قفل می‌کند و همه داده‎های رایانهٔ لوحی را پاک می‌کند."</string>
@@ -532,7 +532,7 @@
     <item msgid="7897544654242874543">"محل کار"</item>
     <item msgid="1103601433382158155">"نمابر محل کار"</item>
     <item msgid="1735177144948329370">"نمابر خانه"</item>
-    <item msgid="603878674477207394">"پیجر"</item>
+    <item msgid="603878674477207394">"پی‌جو"</item>
     <item msgid="1650824275177931637">"سایر موارد"</item>
     <item msgid="9192514806975898961">"سفارشی"</item>
   </string-array>
@@ -575,7 +575,7 @@
     <string name="phoneTypeWork" msgid="8863939667059911633">"محل کار"</string>
     <string name="phoneTypeFaxWork" msgid="3517792160008890912">"نمابر محل کار"</string>
     <string name="phoneTypeFaxHome" msgid="2067265972322971467">"نمابر خانه"</string>
-    <string name="phoneTypePager" msgid="7582359955394921732">"پیجر"</string>
+    <string name="phoneTypePager" msgid="7582359955394921732">"پی‌جو"</string>
     <string name="phoneTypeOther" msgid="1544425847868765990">"سایر موارد"</string>
     <string name="phoneTypeCallback" msgid="2712175203065678206">"برگرداندن تماس"</string>
     <string name="phoneTypeCar" msgid="8738360689616716982">"خودرو"</string>
@@ -700,9 +700,9 @@
     <string name="lockscreen_glogin_too_many_attempts" msgid="2751368605287288808">"‏تلاش‎های زیادی برای کشیدن الگو صورت گرفته است"</string>
     <string name="lockscreen_glogin_instructions" msgid="3931816256100707784">"‏برای بازگشایی قفل، با حساب Google خود وارد سیستم شوید."</string>
     <string name="lockscreen_glogin_username_hint" msgid="8846881424106484447">"نام کاربری (ایمیل)"</string>
-    <string name="lockscreen_glogin_password_hint" msgid="5958028383954738528">"رمز ورود"</string>
+    <string name="lockscreen_glogin_password_hint" msgid="5958028383954738528">"گذرواژه"</string>
     <string name="lockscreen_glogin_submit_button" msgid="7130893694795786300">"ورود به سیستم"</string>
-    <string name="lockscreen_glogin_invalid_input" msgid="1364051473347485908">"نام کاربر یا رمز ورود نامعتبر است."</string>
+    <string name="lockscreen_glogin_invalid_input" msgid="1364051473347485908">"نام کاربر یا گذرواژه نامعتبر است."</string>
     <string name="lockscreen_glogin_account_recovery_hint" msgid="1696924763690379073">"‏نام کاربری یا گذرواژهٔ خود را فراموش کردید؟\nاز "<b>"google.com/accounts/recovery"</b>" بازدید کنید."</string>
     <string name="lockscreen_glogin_checking_password" msgid="7114627351286933867">"در حال بررسی..."</string>
     <string name="lockscreen_unlock_label" msgid="737440483220667054">"بازگشایی قفل"</string>
@@ -786,7 +786,7 @@
     <string name="permdesc_addVoicemail" msgid="6604508651428252437">"به برنامه اجازه می‌دهد تا پیام‌ها را به صندوق دریافت پست صوتی شما اضافه کند."</string>
     <string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"تغییر مجوزهای مکان جغرافیایی مرورگر"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"‏به برنامه اجازه می‎دهد تا مجوزهای جغرافیایی مرورگر را تغییر دهد. برنامه‎های مخرب می‎توانند از آن استفاده کنند تا اطلاعات موقعیت مکانی را به سایت‌های وب کتابخانه بفرستند."</string>
-    <string name="save_password_message" msgid="767344687139195790">"می‌خواهید مرورگر این رمز ورود را به خاطر داشته باشد؟"</string>
+    <string name="save_password_message" msgid="767344687139195790">"می‌خواهید مرورگر این گذرواژه را به خاطر داشته باشد؟"</string>
     <string name="save_password_notnow" msgid="6389675316706699758">"اکنون نه"</string>
     <string name="save_password_remember" msgid="6491879678996749466">"به خاطر سپردن"</string>
     <string name="save_password_never" msgid="8274330296785855105">"هیچ‌وقت"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 72b1c54..a034a25 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -290,7 +290,7 @@
     <string name="permdesc_enableCarMode" msgid="4853187425751419467">"Consente all\'applicazione di abilitare la modalità automobile."</string>
     <string name="permlab_killBackgroundProcesses" msgid="3914026687420177202">"chiusura altre applicazioni"</string>
     <string name="permdesc_killBackgroundProcesses" msgid="4593353235959733119">"Consente all\'applicazione di terminare i processi in background di altre applicazioni. Ciò potrebbe causare l\'interruzione di altre applicazioni."</string>
-    <string name="permlab_systemAlertWindow" msgid="3543347980839518613">"spostamento sopra altre app"</string>
+    <string name="permlab_systemAlertWindow" msgid="3543347980839518613">"posizionamento davanti ad altre app"</string>
     <string name="permdesc_systemAlertWindow" msgid="8584678381972820118">"Consente all\'applicazione di spostarsi sopra ad altre applicazioni o parti dell\'interfaccia utente. Potrebbe interferire con il tuo utilizzo dell\'interfaccia in qualsiasi applicazione o cambiare ciò che credi di vedere in altre applicazioni."</string>
     <string name="permlab_persistentActivity" msgid="8841113627955563938">"esecuzione permanente delle applicazioni"</string>
     <string name="permdesc_persistentActivity" product="tablet" msgid="8525189272329086137">"Consente all\'applicazione di rendere persistenti in memoria alcune sue parti. Ciò può limitare la memoria disponibile per altre applicazioni, rallentando il tablet."</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 29fd6a1..e803cbb 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -163,7 +163,7 @@
     <string name="contentServiceTooManyDeletesNotificationDesc" msgid="8100981435080696431">"För många <xliff:g id="CONTENT_TYPE">%s</xliff:g>-borttagningar."</string>
     <string name="low_memory" product="tablet" msgid="6494019234102154896">"Pekdatorns lagringsutrymme är fullt. Ta bort några filer för att frigöra utrymme."</string>
     <string name="low_memory" product="watch" msgid="4415914910770005166">"Klockans lagringsutrymme är fullt. Ta bort några filer för att frigöra utrymme."</string>
-    <string name="low_memory" product="tv" msgid="516619861191025923">"Lagringsutrymmet på tv:n är fullt. Ta bort några filer för att frigöra utrymme."</string>
+    <string name="low_memory" product="tv" msgid="516619861191025923">"Lagringsutrymmet på TV:n är fullt. Ta bort några filer för att frigöra utrymme."</string>
     <string name="low_memory" product="default" msgid="3475999286680000541">"Mobilens lagringsutrymme är fullt. Ta bort några filer för att frigöra utrymme."</string>
     <string name="ssl_ca_cert_warning" msgid="5848402127455021714">"Nätverket kan vara övervakat"</string>
     <string name="ssl_ca_cert_noti_by_unknown" msgid="4475437862189850602">"Av en okänd tredje part"</string>
@@ -177,7 +177,7 @@
     <string name="factory_reset_message" msgid="4905025204141900666">"Administratörsappen saknar delar eller är skadad och kan inte användas. Enheten kommer nu att rensas. Kontakta administratören om du behöver hjälp."</string>
     <string name="me" msgid="6545696007631404292">"Jag"</string>
     <string name="power_dialog" product="tablet" msgid="8545351420865202853">"Alternativ för surfplattan"</string>
-    <string name="power_dialog" product="tv" msgid="6153888706430556356">"Tv-alternativ"</string>
+    <string name="power_dialog" product="tv" msgid="6153888706430556356">"TV-alternativ"</string>
     <string name="power_dialog" product="default" msgid="1319919075463988638">"Telefonalternativ"</string>
     <string name="silent_mode" msgid="7167703389802618663">"Tyst läge"</string>
     <string name="turn_on_radio" msgid="3912793092339962371">"Aktivera trådlöst"</string>
@@ -195,7 +195,7 @@
     <string name="reboot_to_reset_message" msgid="2432077491101416345">"Startar om …"</string>
     <string name="shutdown_progress" msgid="2281079257329981203">"Avslutar…"</string>
     <string name="shutdown_confirm" product="tablet" msgid="3385745179555731470">"Din surfplatta stängs av."</string>
-    <string name="shutdown_confirm" product="tv" msgid="476672373995075359">"Tv:n stängs av."</string>
+    <string name="shutdown_confirm" product="tv" msgid="476672373995075359">"TV:n stängs av."</string>
     <string name="shutdown_confirm" product="watch" msgid="3490275567476369184">"Klockan stängs av."</string>
     <string name="shutdown_confirm" product="default" msgid="649792175242821353">"Din telefon stängs av."</string>
     <string name="shutdown_confirm_question" msgid="2906544768881136183">"Vill du stänga av?"</string>
@@ -276,7 +276,7 @@
     <string name="permdesc_sendSms" msgid="7094729298204937667">"Tillåter att appen skickar SMS. Detta kan leda till oväntade avgifter. Skadliga appar kan skicka meddelanden utan ditt godkännande vilket kan kosta pengar."</string>
     <string name="permlab_readSms" msgid="8745086572213270480">"läsa dina textmeddelanden (SMS eller MMS)"</string>
     <string name="permdesc_readSms" product="tablet" msgid="2467981548684735522">"Tillåter att appen läser SMS som sparats på surfplattan eller på SIM-kortet. Med den här behörigheten tillåts appen att läsa alla SMS oavsett innehåll eller sekretess."</string>
-    <string name="permdesc_readSms" product="tv" msgid="5102425513647038535">"Tillåter att appen läser sms som har sparats på tv:n eller SIM-kortet. På så sätt kan appen läsa alla sms oavsett innehåll eller sekretess."</string>
+    <string name="permdesc_readSms" product="tv" msgid="5102425513647038535">"Tillåter att appen läser sms som har sparats på TV:n eller SIM-kortet. På så sätt kan appen läsa alla sms oavsett innehåll eller sekretess."</string>
     <string name="permdesc_readSms" product="default" msgid="3695967533457240550">"Tillåter att appen läser SMS som sparats på mobilen eller på SIM-kortet. Med den här behörigheten tillåts appen att läsa alla SMS oavsett innehåll eller sekretess."</string>
     <string name="permlab_receiveWapPush" msgid="5991398711936590410">"ta emot textmeddelanden (WAP)"</string>
     <string name="permdesc_receiveWapPush" msgid="748232190220583385">"Tillåter att appen tar emot och hanterar WAP-meddelanden. Med den här behörigheten kan appen övervaka eller ta bort meddelanden som skickats till dig utan att visa dem för dig."</string>
@@ -294,7 +294,7 @@
     <string name="permdesc_systemAlertWindow" msgid="8584678381972820118">"Tillåter att appen att dras ovanpå andra appar eller delar av användargränssnittet. De kan störa din användning av gränssnittet i olika appar eller ändra vad du tror visas i andra appar."</string>
     <string name="permlab_persistentActivity" msgid="8841113627955563938">"se till att appen alltid körs"</string>
     <string name="permdesc_persistentActivity" product="tablet" msgid="8525189272329086137">"Tillåter att delar av appen läggs beständigt i minnet. Detta kan innebära att det tillgängliga minnet för andra appar begränsas, vilket gör surfplattan långsam."</string>
-    <string name="permdesc_persistentActivity" product="tv" msgid="5086862529499103587">"Tillåter att en app gör vissa delar beständiga i minnet. Det kan begränsa mängden minne som är tillgänglig för andra appar och gör tv:n långsammare."</string>
+    <string name="permdesc_persistentActivity" product="tv" msgid="5086862529499103587">"Tillåter att en app gör vissa delar beständiga i minnet. Det kan begränsa mängden minne som är tillgänglig för andra appar och gör TV:n långsammare."</string>
     <string name="permdesc_persistentActivity" product="default" msgid="4384760047508278272">"Tillåter att delar av appen läggs beständigt i minnet. Detta kan innebära att det tillgängliga minnet för andra appar begränsas, vilket gör mobilen långsam."</string>
     <string name="permlab_getPackageSize" msgid="7472921768357981986">"mäta appens lagringsplats"</string>
     <string name="permdesc_getPackageSize" msgid="3921068154420738296">"Tillåter att appen hämtar kod, data och cachestorlekar"</string>
@@ -306,33 +306,33 @@
     <string name="permdesc_receiveBootCompleted" product="default" msgid="513950589102617504">"Tillåter att appen startar automatiskt när systemet har startats om. Detta kan innebära att det tar längre tid att starta mobilen och att mobilen blir långsammare i och med att appen hela tiden körs i bakgrunden."</string>
     <string name="permlab_broadcastSticky" msgid="7919126372606881614">"Skicka sticky broadcast"</string>
     <string name="permdesc_broadcastSticky" product="tablet" msgid="7749760494399915651">"Tillåter att appen skickar sticky broadcasts, som finns kvar när sändningen är slut. Vid intensiv användning kan mobilen bli långsam eller instabil eftersom minnet överbelastas."</string>
-    <string name="permdesc_broadcastSticky" product="tv" msgid="6839285697565389467">"Tillåter att appen skickar sticky broadcasts som finns kvar när sändningen är slut. Överdriven användning kan göra tv:n seg eller instabil eftersom den använder för mycket minne."</string>
+    <string name="permdesc_broadcastSticky" product="tv" msgid="6839285697565389467">"Tillåter att appen skickar sticky broadcasts som finns kvar när sändningen är slut. Överdriven användning kan göra TV:n seg eller instabil eftersom den använder för mycket minne."</string>
     <string name="permdesc_broadcastSticky" product="default" msgid="2825803764232445091">"Tillåter att appen skickar sticky broadcast, som finns kvar när sändningen är slut. Vid intensiv användning kan mobilen bli långsam eller instabil eftersom minnet överbelastas."</string>
     <string name="permlab_readContacts" msgid="8348481131899886131">"läsa dina kontakter"</string>
     <string name="permdesc_readContacts" product="tablet" msgid="5294866856941149639">"Tillåter att appen läser kontaktuppgifter som sparats på surfplattan, inklusive information om hur ofta du har ringt, skickat e-post till eller på andra sätt kommunicerat med specifika personer. Med den här behörigheten tillåts appen att spara kontaktuppgifter. Skadliga appar kan dela uppgifterna med andra utan din vetskap."</string>
-    <string name="permdesc_readContacts" product="tv" msgid="1839238344654834087">"Tillåter att appen läser data om dina kontakter som sparats på tv:n, bland annat hur ofta du har ringt, skickat e-post eller kommunicerat på andra sätt med enskilda individer. Med den här behörigheten kan appar spara dina kontaktuppgifter och skadliga appar kan dela kontaktuppgifter utan att du vet om det."</string>
+    <string name="permdesc_readContacts" product="tv" msgid="1839238344654834087">"Tillåter att appen läser data om dina kontakter som sparats på TV:n, bland annat hur ofta du har ringt, skickat e-post eller kommunicerat på andra sätt med enskilda individer. Med den här behörigheten kan appar spara dina kontaktuppgifter och skadliga appar kan dela kontaktuppgifter utan att du vet om det."</string>
     <string name="permdesc_readContacts" product="default" msgid="8440654152457300662">"Tillåter att appen läser kontaktuppgifter som sparats på mobilen, inklusive information om hur ofta du har ringt, skickat e-post till eller på andra sätt kommunicerat med specifika personer. Med den här behörigheten tillåts appen att spara kontaktuppgifter. Skadliga appar kan dela uppgifterna med andra utan din vetskap."</string>
     <string name="permlab_writeContacts" msgid="5107492086416793544">"ändra kontakterna"</string>
     <string name="permdesc_writeContacts" product="tablet" msgid="897243932521953602">"Tillåter att appen ändrar kontaktuppgifter som sparats på surfplattan, inklusive information om hur ofta du har ringt, skickat e-post till eller på andra sätt kommunicerat med specifika personer. Med den här behörigheten tillåts appar att ta bort kontaktuppgifter."</string>
-    <string name="permdesc_writeContacts" product="tv" msgid="5438230957000018959">"Tillåter att appen ändrar uppgifterna om dina kontakter som har sparats på tv:n, bland annat hur ofta du har ringt, skickat e-post eller kommunicerat på andra sätt med särskilda kontakter. Med den här behörigheten kan appar ta bort kontaktuppgifter."</string>
+    <string name="permdesc_writeContacts" product="tv" msgid="5438230957000018959">"Tillåter att appen ändrar uppgifterna om dina kontakter som har sparats på TV:n, bland annat hur ofta du har ringt, skickat e-post eller kommunicerat på andra sätt med särskilda kontakter. Med den här behörigheten kan appar ta bort kontaktuppgifter."</string>
     <string name="permdesc_writeContacts" product="default" msgid="589869224625163558">"Tillåter att appen ändrar kontaktuppgifter som sparats på mobilen, inklusive information om hur ofta du har ringt, skickat e-post till eller på andra sätt kommunicerat med specifika personer. Med den här behörigheten tillåts appar att ta bort kontaktuppgifter."</string>
     <string name="permlab_readCallLog" msgid="3478133184624102739">"läs samtalslogg"</string>
     <string name="permdesc_readCallLog" product="tablet" msgid="3700645184870760285">"Tillåter att appen läser pekdatorns samtalslista, inklusive uppgifter om inkommande och utgående samtal. Med den här behörigheten tillåts appen att spara samtalshistoriken. Skadliga appar kan dela uppgifterna med andra utan din vetskap."</string>
-    <string name="permdesc_readCallLog" product="tv" msgid="5611770887047387926">"Tillåter att appen läser tv:ns samtalslista, bland annat data om inkommande och utgående samtal. Med den här behörigheten kan appar spara data i dina samtalslistor och skadliga appar kan dela data i samtalslistor utan att du vet om det."</string>
+    <string name="permdesc_readCallLog" product="tv" msgid="5611770887047387926">"Tillåter att appen läser TV:ns samtalslista, bland annat data om inkommande och utgående samtal. Med den här behörigheten kan appar spara data i dina samtalslistor och skadliga appar kan dela data i samtalslistor utan att du vet om det."</string>
     <string name="permdesc_readCallLog" product="default" msgid="5777725796813217244">"Tillåter att appen läser mobilens samtalslista, inklusive uppgifter om inkommande och utgående samtal. Med den här behörigheten tillåts appen att spara samtalshistoriken. Skadliga appar kan dela uppgifterna med andra utan din vetskap."</string>
     <string name="permlab_writeCallLog" msgid="8552045664743499354">"skriv samtalslogg"</string>
     <string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"Tillåter att appen gör ändringar i pekdatorns samtalslista, inklusive i uppgifter om inkommande och utgående samtal. Skadliga program kan använda detta för att radera eller ändra din samtalslista."</string>
-    <string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"Tillåter att appen gör ändringar i tv:ns samtalslista, inklusive i uppgifter om inkommande och utgående samtal. Skadliga appar kan använda detta för att rensa eller ändra din samtalslista."</string>
+    <string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"Tillåter att appen gör ändringar i TV:ns samtalslista, inklusive i uppgifter om inkommande och utgående samtal. Skadliga appar kan använda detta för att rensa eller ändra din samtalslista."</string>
     <string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"Tillåter att appen gör ändringar i mobilens samtalslista, inklusive i uppgifter om inkommande och utgående samtal. Skadliga program kan använda detta för att radera eller ändra din samtalslista."</string>
     <string name="permlab_bodySensors" msgid="4871091374767171066">"kroppssens. (för hjärtat m.m.)"</string>
     <string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"Ger appen åtkomst till information från sensorer om ditt fysiska tillstånd, till exempel din puls."</string>
     <string name="permlab_readCalendar" msgid="5972727560257612398">"läsa kalenderuppgifter plus konfidentiell information"</string>
     <string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"Tillåter att appen läser alla kalenderuppgifter som sparats på surfplattan, inklusive dina vänners eller kollegors uppgifter. Med den här behörigheten kan appen tillåtas att dela eller spara kalenderuppgifter även om de är sekretessbelagda eller känsliga."</string>
-    <string name="permdesc_readCalendar" product="tv" msgid="3191352452242394196">"Tillåter att appen läser alla kalenderhändelser som sparats på tv:n, bland annat de som tillhör vänner eller kollegor. På så sätt kan appen dela eller spara dina kalenderhändelser oavsett sekretess eller känslighet."</string>
+    <string name="permdesc_readCalendar" product="tv" msgid="3191352452242394196">"Tillåter att appen läser alla kalenderhändelser som sparats på TV:n, bland annat de som tillhör vänner eller kollegor. På så sätt kan appen dela eller spara dina kalenderhändelser oavsett sekretess eller känslighet."</string>
     <string name="permdesc_readCalendar" product="default" msgid="7434548682470851583">"Tillåter att appen läser alla kalenderuppgifter som sparats på mobilen, inklusive dina vänners eller kollegors uppgifter. Med den här behörigheten kan appen tillåtas att dela eller spara kalenderuppgifter även om de är sekretessbelagda eller känsliga."</string>
     <string name="permlab_writeCalendar" msgid="8438874755193825647">"lägga till eller ändra kalenderuppgifter och skicka e-post till gäster utan ägarens vetskap"</string>
     <string name="permdesc_writeCalendar" product="tablet" msgid="6679035520113668528">"Tillåter att appen lägger till, tar bort och ändrar sådana händelser som du kan ändra på surfplattan, inklusive dina vänners eller kollegors uppgifter. Detta kan innebära att appen tillåts skicka meddelanden som ser ut att komma från kalenderns ägare eller ändra händelser utan ägarens vetskap."</string>
-    <string name="permdesc_writeCalendar" product="tv" msgid="1273290605500902507">"Tillåter att appen lägger till, tar bort och ändrar händelser som du kan ändra på tv:n, inklusive dina vänners eller kollegors uppgifter. Appen kan på så sätt skicka meddelanden som ser ut att komma från kalenderns ägare eller ändra uppgifter utan ägarens vetskap."</string>
+    <string name="permdesc_writeCalendar" product="tv" msgid="1273290605500902507">"Tillåter att appen lägger till, tar bort och ändrar händelser som du kan ändra på TV:n, inklusive dina vänners eller kollegors uppgifter. Appen kan på så sätt skicka meddelanden som ser ut att komma från kalenderns ägare eller ändra uppgifter utan ägarens vetskap."</string>
     <string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"Tillåter att appen lägger till, tar bort och ändrar sådana händelser som du kan ändra på mobilen, inklusive dina vänners eller kollegors uppgifter. Detta kan innebära att appen tillåts skicka meddelanden som ser ut att komma från kalenderns ägare eller ändra händelser utan ägarens vetskap."</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"få åtkomst till extra kommandon för platsleverantör"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Tillåter att appen får åtkomst till extra kommandon för platsleverantör. Detta kan innebära att appen tillåts störa funktionen för GPS eller andra platskällor."</string>
@@ -359,14 +359,14 @@
     <string name="permlab_readPhoneState" msgid="9178228524507610486">"läsa telefonens status och identitet"</string>
     <string name="permdesc_readPhoneState" msgid="1639212771826125528">"Tillåter att appen kommer åt enhetens telefonfunktioner. Med den här behörigheten tillåts appen att identifiera mobilens telefonnummer och enhets-ID, om ett samtal pågår och vilket nummer samtalet är kopplat till."</string>
     <string name="permlab_wakeLock" product="tablet" msgid="1531731435011495015">"förhindra att surfplattan går in i viloläge"</string>
-    <string name="permlab_wakeLock" product="tv" msgid="2601193288949154131">"förhindra att tv:n försätts i viloläge"</string>
+    <string name="permlab_wakeLock" product="tv" msgid="2601193288949154131">"förhindra att TV:n försätts i viloläge"</string>
     <string name="permlab_wakeLock" product="default" msgid="573480187941496130">"förhindra att telefonen sätts i viloläge"</string>
     <string name="permdesc_wakeLock" product="tablet" msgid="7311319824400447868">"Tillåter att appen förhindrar att surfplattan går in i viloläge."</string>
-    <string name="permdesc_wakeLock" product="tv" msgid="3208534859208996974">"Tillåter att appen förhindrar att tv:n försätts i viloläge."</string>
+    <string name="permdesc_wakeLock" product="tv" msgid="3208534859208996974">"Tillåter att appen förhindrar att TV:n försätts i viloläge."</string>
     <string name="permdesc_wakeLock" product="default" msgid="8559100677372928754">"Tillåter att appen förhindrar att mobilen går in i viloläge."</string>
     <string name="permlab_transmitIr" msgid="7545858504238530105">"tillåt IR-sändning"</string>
     <string name="permdesc_transmitIr" product="tablet" msgid="5358308854306529170">"Tillåter att appen använder surfplattans IR-sändare."</string>
-    <string name="permdesc_transmitIr" product="tv" msgid="3926790828514867101">"Tillåter att appen använder den infraröda sändaren på tv:n."</string>
+    <string name="permdesc_transmitIr" product="tv" msgid="3926790828514867101">"Tillåter att appen använder den infraröda sändaren på TV:n."</string>
     <string name="permdesc_transmitIr" product="default" msgid="7957763745020300725">"Tillåter att appen använder mobilens IR-sändare."</string>
     <string name="permlab_setWallpaper" msgid="6627192333373465143">"ange bakgrund"</string>
     <string name="permdesc_setWallpaper" msgid="7373447920977624745">"Tillåter att appen anger systemets bakgrund."</string>
@@ -374,11 +374,11 @@
     <string name="permdesc_setWallpaperHints" msgid="8235784384223730091">"Tillåter att appen ger tips om systemets bakgrundsstorlek."</string>
     <string name="permlab_setTimeZone" msgid="2945079801013077340">"ange tidszon"</string>
     <string name="permdesc_setTimeZone" product="tablet" msgid="1676983712315827645">"Tillåter att appen ändrar pekdatorns tidszon."</string>
-    <string name="permdesc_setTimeZone" product="tv" msgid="888864653946175955">"Tillåter att appen ändrar tidszonen på tv:n."</string>
+    <string name="permdesc_setTimeZone" product="tv" msgid="888864653946175955">"Tillåter att appen ändrar tidszonen på TV:n."</string>
     <string name="permdesc_setTimeZone" product="default" msgid="4499943488436633398">"Tillåter att appen ändrar mobilens tidszon."</string>
     <string name="permlab_getAccounts" msgid="1086795467760122114">"hitta konton på enheten"</string>
     <string name="permdesc_getAccounts" product="tablet" msgid="2741496534769660027">"Tillåter att appen hämtar en lista över alla kända konton på surfplattan. Detta kan inkludera konton som har skapats av appar som du har installerat."</string>
-    <string name="permdesc_getAccounts" product="tv" msgid="4190633395633907543">"Tillåter att appen hämtar listan med konton som tv:n kan identifiera. Den kan innehålla konton som skapats av appar som du har installerat."</string>
+    <string name="permdesc_getAccounts" product="tv" msgid="4190633395633907543">"Tillåter att appen hämtar listan med konton som TV:n kan identifiera. Den kan innehålla konton som skapats av appar som du har installerat."</string>
     <string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"Tillåter att appen hämtar en lista över alla kända konton på mobilen. Detta kan inkludera konton som har skapats av appar som du har installerat."</string>
     <string name="permlab_accessNetworkState" msgid="4951027964348974773">"visa nätverksanslutningar"</string>
     <string name="permdesc_accessNetworkState" msgid="8318964424675960975">"Tillåter att appen kommer åt information om nätverksanslutningarna, till exempel vilka nätverk som finns och är anslutna."</string>
@@ -394,21 +394,21 @@
     <string name="permdesc_changeWifiState" msgid="7137950297386127533">"Tillåter att appen ansluter till och kopplar från Wi-Fi-åtkomstpunkter samt gör ändringar i enhetens konfiguration för Wi-Fi-nätverk."</string>
     <string name="permlab_changeWifiMulticastState" msgid="1368253871483254784">"tillåt Wi-Fi multicast-mottagning"</string>
     <string name="permdesc_changeWifiMulticastState" product="tablet" msgid="7969774021256336548">"Tillåter att appen tar emot paket som skickats med multicast-adress till alla enheter i ett Wi-Fi-nätverk och inte bara till den här surfplattan. Detta drar mer batteri än när multicastläget inte används."</string>
-    <string name="permdesc_changeWifiMulticastState" product="tv" msgid="9031975661145014160">"Tillåter att appen tar emot paket som skickats till alla enheter i ett Wi-Fi-nätverk med multicastadress, inte bara till tv:n. Detta drar mer batteri än när multicastläget inte används."</string>
+    <string name="permdesc_changeWifiMulticastState" product="tv" msgid="9031975661145014160">"Tillåter att appen tar emot paket som skickats till alla enheter i ett Wi-Fi-nätverk med multicastadress, inte bara till TV:n. Detta drar mer batteri än när multicastläget inte används."</string>
     <string name="permdesc_changeWifiMulticastState" product="default" msgid="6851949706025349926">"Tillåter att appen tar emot paket som skickats med multicast-adress till alla enheter i ett Wi-Fi-nätverk och inte bara till den här mobilen. Detta drar mer batteri än när multicastläget inte används."</string>
     <string name="permlab_bluetoothAdmin" msgid="6006967373935926659">"få åtkomst till Bluetooth-inställningar"</string>
     <string name="permdesc_bluetoothAdmin" product="tablet" msgid="6921177471748882137">"Tillåter att appen konfigurerar den lokala Bluetooth-surfplattan samt upptäcker och parkopplar den med fjärranslutna enheter."</string>
-    <string name="permdesc_bluetoothAdmin" product="tv" msgid="3373125682645601429">"Tillåter att appen konfigurerar den lokala Bluetooth-tv:n och identifierar och kopplar den till fjärrenheter."</string>
+    <string name="permdesc_bluetoothAdmin" product="tv" msgid="3373125682645601429">"Tillåter att appen konfigurerar den lokala Bluetooth-TV:n och identifierar och kopplar den till fjärrenheter."</string>
     <string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"Tillåter att appen konfigurerar den lokala Bluetooth-mobilen samt upptäcker och parkopplar den med fjärranslutna enheter."</string>
     <string name="permlab_accessWimaxState" msgid="4195907010610205703">"ansluta till och koppla från WiMAX"</string>
     <string name="permdesc_accessWimaxState" msgid="6360102877261978887">"Tillåter att appen avgör om WiMAX är aktiverat och kommer åt information om eventuella anslutna WiMAX-nätverk."</string>
     <string name="permlab_changeWimaxState" msgid="2405042267131496579">"ändra WiMAX-status"</string>
     <string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Tillåter att appen ansluter surfplattan till eller kopplar från WiMAX-nätverk."</string>
-    <string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"Tillåter att appen ansluter tv:n till och kopplar från tv:n från WiMAX-nätverk."</string>
+    <string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"Tillåter att appen ansluter TV:n till och kopplar från TV:n från WiMAX-nätverk."</string>
     <string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Tillåter att appen ansluter mobilen till eller kopplar från WiMAX-nätverk."</string>
     <string name="permlab_bluetooth" msgid="6127769336339276828">"koppla till Bluetooth-enheter"</string>
     <string name="permdesc_bluetooth" product="tablet" msgid="3480722181852438628">"Tillåter att appen kommer åt pekdatorns Bluetooth-konfiguration och upprättar och godkänner anslutningar till parkopplade enheter."</string>
-    <string name="permdesc_bluetooth" product="tv" msgid="3974124940101104206">"Tillåter att appen visar konfigurationen av Bluetooth på tv:n och godkänner alla anslutningar till kopplade enheter."</string>
+    <string name="permdesc_bluetooth" product="tv" msgid="3974124940101104206">"Tillåter att appen visar konfigurationen av Bluetooth på TV:n och godkänner alla anslutningar till kopplade enheter."</string>
     <string name="permdesc_bluetooth" product="default" msgid="3207106324452312739">"Tillåter att appen kommer åt mobilens Bluetooth-konfiguration och upprättar och godkänner anslutningar till parkopplade enheter."</string>
     <string name="permlab_nfc" msgid="4423351274757876953">"kontrollera närfältskommunikationen"</string>
     <string name="permdesc_nfc" msgid="7120611819401789907">"Tillåter att appen kommunicerar med etiketter, kort och läsare för närfältskommunikation (NFC)."</string>
@@ -499,10 +499,10 @@
     <string name="policydesc_limitPassword" msgid="2502021457917874968">"Styr tillåten längd och tillåtna tecken i lösenord och pinkoder för skärmlåset."</string>
     <string name="policylab_watchLogin" msgid="914130646942199503">"Övervaka försök att låsa upp skärmen"</string>
     <string name="policydesc_watchLogin" product="tablet" msgid="3215729294215070072">"Övervaka antalet felaktiga lösenord som angetts för skärmlåset och lås surfplattan eller ta bort alla data från surfplattan om för många felaktiga försök görs."</string>
-    <string name="policydesc_watchLogin" product="TV" msgid="2707817988309890256">"Övervakar antalet felaktiga lösenord som skrivits in vid upplåsning av skärmen och låser tv:n eller rensar alla uppgifter på tv:n om för många felaktiga lösenord har skrivits in."</string>
+    <string name="policydesc_watchLogin" product="TV" msgid="2707817988309890256">"Övervakar antalet felaktiga lösenord som skrivits in vid upplåsning av skärmen och låser TV:n eller rensar alla uppgifter på TV:n om för många felaktiga lösenord har skrivits in."</string>
     <string name="policydesc_watchLogin" product="default" msgid="5712323091846761073">"Övervaka antalet felaktiga lösenord som angivits för skärmlåset och lås mobilen eller ta bort alla data från mobilen om för många felaktiga försök görs."</string>
     <string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="4280246270601044505">"Övervaka antalet felaktiga lösenord som skrivits in vid upplåsning av skärmen och lås surfplattan eller rensa alla uppgifter för den här användaren om för många felaktiga lösenord har skrivits in."</string>
-    <string name="policydesc_watchLogin_secondaryUser" product="TV" msgid="3484832653564483250">"Övervaka antalet felaktiga lösenord som skrivits in vid upplåsning av skärmen och lås tv:n eller rensa alla uppgifter för den här användaren om för många felaktiga lösenord har skrivits in."</string>
+    <string name="policydesc_watchLogin_secondaryUser" product="TV" msgid="3484832653564483250">"Övervaka antalet felaktiga lösenord som skrivits in vid upplåsning av skärmen och lås TV:n eller rensa alla uppgifter för den här användaren om för många felaktiga lösenord har skrivits in."</string>
     <string name="policydesc_watchLogin_secondaryUser" product="default" msgid="2185480427217127147">"Övervaka antalet felaktiga lösenord som skrivits in vid upplåsning av skärmen och lås mobilen eller rensa alla uppgifter för den här användaren om för många felaktiga lösenord har skrivits in."</string>
     <string name="policylab_resetPassword" msgid="4934707632423915395">"Ändra skärmlåset"</string>
     <string name="policydesc_resetPassword" msgid="1278323891710619128">"Ändra skärmlåset."</string>
@@ -510,11 +510,11 @@
     <string name="policydesc_forceLock" msgid="1141797588403827138">"Kontrollera hur och när skärmlåset aktiveras."</string>
     <string name="policylab_wipeData" msgid="3910545446758639713">"Radera all data"</string>
     <string name="policydesc_wipeData" product="tablet" msgid="4306184096067756876">"Ta bort data från surfplattan utan förvarning genom att återställa standardinställningarna."</string>
-    <string name="policydesc_wipeData" product="tv" msgid="5816221315214527028">"Rensar uppgifterna på tv:n utan föregående varning genom att återställa standardinställningarna."</string>
+    <string name="policydesc_wipeData" product="tv" msgid="5816221315214527028">"Rensar uppgifterna på TV:n utan föregående varning genom att återställa standardinställningarna."</string>
     <string name="policydesc_wipeData" product="default" msgid="5096895604574188391">"Ta bort data från mobilen utan förvarning genom att återställa standardinställningarna."</string>
     <string name="policylab_wipeData_secondaryUser" msgid="8362863289455531813">"Radera användaruppgifter"</string>
     <string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="6336255514635308054">"Rensa användarens uppgifter på den här surfplattan utan förvarning."</string>
-    <string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2086473496848351810">"Rensa användarens uppgifter på den här tv:n utan förvarning."</string>
+    <string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2086473496848351810">"Rensa användarens uppgifter på den här TV:n utan förvarning."</string>
     <string name="policydesc_wipeData_secondaryUser" product="default" msgid="6787904546711590238">"Rensa användarens data på den här mobilen utan förvarning."</string>
     <string name="policylab_setGlobalProxy" msgid="2784828293747791446">"Ange global proxyserver"</string>
     <string name="policydesc_setGlobalProxy" msgid="8459859731153370499">"Ange enhetens globala proxy som ska användas när policyn aktiveras. Det är bara enhetens ägare som kan ange global proxy."</string>
@@ -663,7 +663,7 @@
     <string name="faceunlock_multiple_failures" msgid="754137583022792429">"Du har försökt låsa upp med Ansiktslås för många gånger"</string>
     <string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"Inget SIM-kort"</string>
     <string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"Inget SIM-kort i surfplattan."</string>
-    <string name="lockscreen_missing_sim_message" product="tv" msgid="1943633865476989599">"Det finns inget SIM-kort i tv:n."</string>
+    <string name="lockscreen_missing_sim_message" product="tv" msgid="1943633865476989599">"Det finns inget SIM-kort i TV:n."</string>
     <string name="lockscreen_missing_sim_message" product="default" msgid="2186920585695169078">"Inget SIM-kort i telefonen."</string>
     <string name="lockscreen_missing_sim_instructions" msgid="5372787138023272615">"Sätt i ett SIM-kort."</string>
     <string name="lockscreen_missing_sim_instructions_long" msgid="3526573099019319472">"SIM-kort saknas eller kan inte läsas. Sätt i ett SIM-kort."</string>
@@ -686,13 +686,13 @@
     <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Du har angett fel lösenord <xliff:g id="NUMBER_0">%d</xliff:g> gånger. \n\nFörsök igen om <xliff:g id="NUMBER_1">%d</xliff:g> sekunder."</string>
     <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Du har angett fel lösenord <xliff:g id="NUMBER_0">%d</xliff:g> gånger. \n\nFörsök igen om <xliff:g id="NUMBER_1">%d</xliff:g> sekunder."</string>
     <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Du har ritat ditt grafiska lösenord fel <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter <xliff:g id="NUMBER_1">%d</xliff:g> försök till ombeds du att låsa upp surfplattan med din Google-inloggning.\n\n Försök igen om  <xliff:g id="NUMBER_2">%d</xliff:g> sekunder."</string>
-    <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Du har ritat fel mönster för upplåsning <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter ytterligare <xliff:g id="NUMBER_1">%d</xliff:g> misslyckade försök blir du ombedd att låsa upp tv:n genom att logga in på Google.\n\n Försök igen om <xliff:g id="NUMBER_2">%d</xliff:g> sekunder."</string>
+    <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Du har ritat fel mönster för upplåsning <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter ytterligare <xliff:g id="NUMBER_1">%d</xliff:g> misslyckade försök blir du ombedd att låsa upp TV:n genom att logga in på Google.\n\n Försök igen om <xliff:g id="NUMBER_2">%d</xliff:g> sekunder."</string>
     <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Du har ritat ditt grafiska lösenord fel <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter <xliff:g id="NUMBER_1">%d</xliff:g> försök till ombeds du att låsa upp mobilen med uppgifterna som du använder när du loggar in på Google.\n\n Försök igen om  <xliff:g id="NUMBER_2">%d</xliff:g> sekunder."</string>
     <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Du har försökt låsa upp surfplattan på fel sätt <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter <xliff:g id="NUMBER_1">%d</xliff:g> misslyckade försök till kommer surfplattan att återställas till fabriksinställningarna. Du förlorar då alla användardata."</string>
-    <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Du har misslyckats med att låsa upp tv:n <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter ytterligare <xliff:g id="NUMBER_1">%d</xliff:g> misslyckade försök kommer tv:n att återställas till standardinställningarna och alla användaruppgifter kommer att gå förlorade."</string>
+    <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Du har misslyckats med att låsa upp TV:n <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter ytterligare <xliff:g id="NUMBER_1">%d</xliff:g> misslyckade försök kommer TV:n att återställas till standardinställningarna och alla användaruppgifter kommer att gå förlorade."</string>
     <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Du har försökt låsa upp mobilen på fel sätt <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter <xliff:g id="NUMBER_1">%d</xliff:g> misslyckade försök till kommer mobilen att återställas till fabriksinställningarna. Du förlorar då alla användardata."</string>
     <string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"Du har försökt låsa upp surfplattan på fel sätt <xliff:g id="NUMBER">%d</xliff:g> gånger. Surfplattan återställs nu till fabriksinställningarna."</string>
-    <string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"Du har misslyckats med att låsa upp tv:n <xliff:g id="NUMBER">%d</xliff:g> gånger. Tv:n kommer nu att återställas till standardinställningarna."</string>
+    <string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"Du har misslyckats med att låsa upp TV:n <xliff:g id="NUMBER">%d</xliff:g> gånger. TV:n kommer nu att återställas till standardinställningarna."</string>
     <string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"Du har försökt låsa upp mobilen på fel sätt <xliff:g id="NUMBER">%d</xliff:g> gånger. Mobilen återställs nu till fabriksinställningarna."</string>
     <string name="lockscreen_too_many_failed_attempts_countdown" msgid="6251480343394389665">"Försök igen om <xliff:g id="NUMBER">%d</xliff:g> sekunder."</string>
     <string name="lockscreen_forgot_pattern_button_text" msgid="2626999449610695930">"Glömt ditt grafiska lösenord?"</string>
@@ -778,7 +778,7 @@
     <string name="permdesc_readHistoryBookmarks" msgid="8462378226600439658">"Tillåter att appen läser historiken för besökta sidor och alla bokmärken i webbläsaren. Observera att den här behörigheten kanske inte är tillämplig för webbläsare från tredje part eller andra appar med surffunktion."</string>
     <string name="permlab_writeHistoryBookmarks" msgid="3714785165273314490">"skriva bokmärken och historik på webben"</string>
     <string name="permdesc_writeHistoryBookmarks" product="tablet" msgid="6825527469145760922">"Tillåter att appen ändrar historiken för besökta sidor i webbläsaren eller bokmärken som sparats på surfplattan. Det kan innebära att appen kan ta bort eller ändra webbläsarinformation. Observera att den här behörigheten kanske inte är tillämplig för webbläsare från tredje part eller andra appar med surffunktion."</string>
-    <string name="permdesc_writeHistoryBookmarks" product="tv" msgid="7007393823197766548">"Tillåter att appen ändrar webbläsarens historik eller bokmärken som har sparats på tv:n. Appen kan därmed rensa eller ändra uppgifter i webbläsaren. Obs! Den här behörigheten kanske inte gäller för webbläsare från tredje part eller andra appar med webbfunktioner."</string>
+    <string name="permdesc_writeHistoryBookmarks" product="tv" msgid="7007393823197766548">"Tillåter att appen ändrar webbläsarens historik eller bokmärken som har sparats på TV:n. Appen kan därmed rensa eller ändra uppgifter i webbläsaren. Obs! Den här behörigheten kanske inte gäller för webbläsare från tredje part eller andra appar med webbfunktioner."</string>
     <string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"Tillåter att appen ändrar historiken för besökta sidor i webbläsaren eller bokmärken som sparats på telefonen. Det kan innebära att appen kan ta bort eller ändra webbläsarinformation. Observera att den här behörigheten kanske inte är tillämplig för webbläsare från tredje part eller andra appar med surffunktion."</string>
     <string name="permlab_setAlarm" msgid="1379294556362091814">"ställa in ett alarm"</string>
     <string name="permdesc_setAlarm" msgid="316392039157473848">"Tillåter att appen ställer in ett alarm i en befintlig alarmapp. Vissa alarmappar har inte den här funktionen."</string>
@@ -988,7 +988,7 @@
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Ange den obligatoriska PIN-koden:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN-kod:"</string>
     <string name="wifi_p2p_frequency_conflict_message" product="tablet" msgid="8012981257742232475">"Surfplattans Wi-Fi-anslutning kommer tillfälligt att avbrytas när den är ansluten till <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
-    <string name="wifi_p2p_frequency_conflict_message" product="tv" msgid="3087858235069421128">"Tv:n kopplas tillfälligt från Wi-Fi-nätverket när den är ansluten till <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="wifi_p2p_frequency_conflict_message" product="tv" msgid="3087858235069421128">"TV:n kopplas tillfälligt från Wi-Fi-nätverket när den är ansluten till <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Mobilen kommer tillfälligt att kopplas från Wi-Fi när den är ansluten till <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="select_character" msgid="3365550120617701745">"Infoga tecken"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Skickar SMS"</string>
@@ -1289,13 +1289,13 @@
     <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Du har angett fel lösenord <xliff:g id="NUMBER_0">%d</xliff:g> gånger. \n\nFörsök igen om <xliff:g id="NUMBER_1">%d</xliff:g> sekunder."</string>
     <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Du har ritat ditt grafiska lösenord fel <xliff:g id="NUMBER_0">%d</xliff:g> gånger. \n\nFörsök igen om <xliff:g id="NUMBER_1">%d</xliff:g> sekunder."</string>
     <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Du har försökt låsa upp mobilen på fel sätt <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter ytterligare <xliff:g id="NUMBER_1">%d</xliff:g> misslyckade försök återställs surfplattan till fabriksinställningarna. Du förlorar då alla användardata."</string>
-    <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Du har misslyckats med att låsa upp tv:n <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter ytterligare <xliff:g id="NUMBER_1">%d</xliff:g> misslyckade försök kommer tv:n att återställas till standardinställningarna och alla användaruppgifter kommer att gå förlorade."</string>
+    <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Du har misslyckats med att låsa upp TV:n <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter ytterligare <xliff:g id="NUMBER_1">%d</xliff:g> misslyckade försök kommer TV:n att återställas till standardinställningarna och alla användaruppgifter kommer att gå förlorade."</string>
     <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Du har försökt låsa upp mobilen på fel sätt <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter ytterligare <xliff:g id="NUMBER_1">%d</xliff:g> misslyckade försök återställs mobilen till fabriksinställningarna. Du förlorar då alla användardata."</string>
     <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Du har försökt låsa upp surfplattan på fel sätt <xliff:g id="NUMBER">%d</xliff:g> gånger. Surfplattan återställs nu till fabriksinställningarna."</string>
-    <string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"Du har misslyckats med att låsa upp tv:n <xliff:g id="NUMBER">%d</xliff:g> gånger. Tv:n kommer nu att återställas till standardinställningarna."</string>
+    <string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"Du har misslyckats med att låsa upp TV:n <xliff:g id="NUMBER">%d</xliff:g> gånger. TV:n kommer nu att återställas till standardinställningarna."</string>
     <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Du har försökt låsa upp mobilen på fel sätt <xliff:g id="NUMBER">%d</xliff:g> gånger. Mobilen återställs nu till fabriksinställningarna."</string>
     <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Du har ritat ditt grafiska lösenord fel <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter ytterligare <xliff:g id="NUMBER_1">%d</xliff:g> försök ombeds du låsa upp surfplattan med ett e-postkonto.\n\n Försök igen om <xliff:g id="NUMBER_2">%d</xliff:g> sekunder."</string>
-    <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Du har ritat fel mönster för upplåsning <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter ytterligare <xliff:g id="NUMBER_1">%d</xliff:g> misslyckade försök blir du ombedd att låsa upp tv:n via ett e-postkonto.\n\n Försök igen om <xliff:g id="NUMBER_2">%d</xliff:g> sekunder."</string>
+    <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Du har ritat fel mönster för upplåsning <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter ytterligare <xliff:g id="NUMBER_1">%d</xliff:g> misslyckade försök blir du ombedd att låsa upp TV:n via ett e-postkonto.\n\n Försök igen om <xliff:g id="NUMBER_2">%d</xliff:g> sekunder."</string>
     <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Du har ritat ditt grafiska lösenord fel <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter ytterligare <xliff:g id="NUMBER_1">%d</xliff:g> försök ombeds du låsa upp mobilen med hjälp av ett e-postkonto.\n\n Försök igen om <xliff:g id="NUMBER_2">%d</xliff:g> sekunder."</string>
     <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" – "</string>
     <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Ta bort"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index d367887..af04a46 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -589,7 +589,7 @@
     <string name="phoneTypeTelex" msgid="3367879952476250512">"Teleksi"</string>
     <string name="phoneTypeTtyTdd" msgid="8606514378585000044">"TTY TDD"</string>
     <string name="phoneTypeWorkMobile" msgid="1311426989184065709">"Nambari ya Simu ya Mkononi ya Kazini"</string>
-    <string name="phoneTypeWorkPager" msgid="649938731231157056">"Kiunda ujumbe cha Kazini"</string>
+    <string name="phoneTypeWorkPager" msgid="649938731231157056">"Peja ya Kazini"</string>
     <string name="phoneTypeAssistant" msgid="5596772636128562884">"Msaidizi"</string>
     <string name="phoneTypeMms" msgid="7254492275502768992">"MMS"</string>
     <string name="eventTypeCustom" msgid="7837586198458073404">"Maalum"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 439b9f2..83b0d18 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -530,8 +530,8 @@
     <item msgid="8901098336658710359">"Nhà riêng"</item>
     <item msgid="869923650527136615">"Di Động"</item>
     <item msgid="7897544654242874543">"Cơ quan"</item>
-    <item msgid="1103601433382158155">"Số fax Cơ quan"</item>
-    <item msgid="1735177144948329370">"Số fax Nhà riêng"</item>
+    <item msgid="1103601433382158155">"Số fax cơ quan"</item>
+    <item msgid="1735177144948329370">"Số fax nhà riêng"</item>
     <item msgid="603878674477207394">"Số máy nhắn tin"</item>
     <item msgid="1650824275177931637">"Khác"</item>
     <item msgid="9192514806975898961">"Tùy chỉnh"</item>
@@ -573,8 +573,8 @@
     <string name="phoneTypeHome" msgid="2570923463033985887">"Nhà riêng"</string>
     <string name="phoneTypeMobile" msgid="6501463557754751037">"Di Động"</string>
     <string name="phoneTypeWork" msgid="8863939667059911633">"Cơ quan"</string>
-    <string name="phoneTypeFaxWork" msgid="3517792160008890912">"Số fax Cơ quan"</string>
-    <string name="phoneTypeFaxHome" msgid="2067265972322971467">"Số fax Nhà riêng"</string>
+    <string name="phoneTypeFaxWork" msgid="3517792160008890912">"Số fax cơ quan"</string>
+    <string name="phoneTypeFaxHome" msgid="2067265972322971467">"Số fax nhà riêng"</string>
     <string name="phoneTypePager" msgid="7582359955394921732">"Số máy nhắn tin"</string>
     <string name="phoneTypeOther" msgid="1544425847868765990">"Khác"</string>
     <string name="phoneTypeCallback" msgid="2712175203065678206">"Số gọi lại"</string>
@@ -586,8 +586,8 @@
     <string name="phoneTypeRadio" msgid="4093738079908667513">"Radio"</string>
     <string name="phoneTypeTelex" msgid="3367879952476250512">"Số telex"</string>
     <string name="phoneTypeTtyTdd" msgid="8606514378585000044">"TTY TDD"</string>
-    <string name="phoneTypeWorkMobile" msgid="1311426989184065709">"Số điện thoại di động tại Cơ quan"</string>
-    <string name="phoneTypeWorkPager" msgid="649938731231157056">"Số Máy nhắn tin tại Cơ quan"</string>
+    <string name="phoneTypeWorkMobile" msgid="1311426989184065709">"Di động tại cơ quan"</string>
+    <string name="phoneTypeWorkPager" msgid="649938731231157056">"Số máy nhắn tin cơ quan"</string>
     <string name="phoneTypeAssistant" msgid="5596772636128562884">"Số điện thoại Hỗ trợ"</string>
     <string name="phoneTypeMms" msgid="7254492275502768992">"MMS"</string>
     <string name="eventTypeCustom" msgid="7837586198458073404">"Tùy chỉnh"</string>
diff --git a/core/res/res/values/colors_holo.xml b/core/res/res/values/colors_holo.xml
index 9d1dda5..917c781 100644
--- a/core/res/res/values/colors_holo.xml
+++ b/core/res/res/values/colors_holo.xml
@@ -78,14 +78,14 @@
     <!-- Forward compatibility for Material-style theme colors -->
     <eat-comment />
 
-    <color name="holo_primary_dark">#ff000000</color>
+    <color name="holo_primary_dark">@color/black</color>
     <color name="holo_primary">#ff222222</color>
     <color name="holo_control_activated">@color/holo_blue_light</color>
     <color name="holo_control_normal">#39cccccc</color>
     <color name="holo_button_pressed">#59f0f0f0</color>
     <color name="holo_button_normal">#bd292f34</color>
 
-    <color name="holo_light_primary_dark">#ff000000</color>
+    <color name="holo_light_primary_dark">@color/black</color>
     <color name="holo_light_primary">#ffe6e6e6</color>
     <color name="holo_light_control_activated">@color/holo_blue_light</color>
     <color name="holo_light_control_normal">#dacccccc</color>
diff --git a/core/res/res/values/colors_legacy.xml b/core/res/res/values/colors_legacy.xml
index ad22845..a3ce652 100644
--- a/core/res/res/values/colors_legacy.xml
+++ b/core/res/res/values/colors_legacy.xml
@@ -17,6 +17,12 @@
 <!-- Colors specific to pre-Holo themes. -->
 <resources>
 
+    <!-- A bright Android-y green -->
+    <color name="legacy_green">#ff90df25</color>
+
+    <!-- A bright orange suitable for use in the early 2000s -->
+    <color name="legacy_orange">#fffea50b</color>
+
     <!-- Highlight colors for the legacy themes -->
     <eat-comment />
 
@@ -27,19 +33,17 @@
     <!-- Forward compatibility for Material-style theme colors -->
     <eat-comment />
 
-    <color name="legacy_primary_dark">#ff000000</color>
-    <color name="legacy_primary">#ffe6e6e6</color>
-    <color name="legacy_primary_light">#ffffffff</color>
-    <color name="legacy_control_activated">#ff90df25</color>
+    <color name="legacy_primary_dark">@color/black</color>
+    <color name="legacy_primary">#ff222222</color>
+    <color name="legacy_control_activated">@color/legacy_green</color>
     <color name="legacy_control_normal">#99ffffff</color>
-    <color name="legacy_button_pressed">#fffea50b</color>
+    <color name="legacy_button_pressed">@color/legacy_orange</color>
     <color name="legacy_button_normal">#f3dbdbdb</color>
 
-    <color name="legacy_light_primary_dark">@color/legacy_primary_dark</color>
-    <color name="legacy_light_primary">@color/legacy_primary</color>
-    <color name="legacy_light_primary_light">@color/legacy_primary_light</color>
-    <color name="legacy_light_control_activated">@color/legacy_control_activated</color>
+    <color name="legacy_light_primary_dark">@color/black</color>
+    <color name="legacy_light_primary">#ffe6e6e6</color>
+    <color name="legacy_light_control_activated">@color/legacy_green</color>
     <color name="legacy_light_control_normal">#99000000</color>
-    <color name="legacy_light_button_pressed">@color/legacy_button_pressed</color>
-    <color name="legacy_light_button_normal">@color/legacy_button_normal</color>
+    <color name="legacy_light_button_pressed">@color/legacy_orange</color>
+    <color name="legacy_light_button_normal">#f3dbdbdb</color>
 </resources>
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index 38a1693..58640eb 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -307,9 +307,10 @@
     <style name="TextAppearance.Material.Widget.PopupMenu"/>
     <style name="TextAppearance.Material.Widget.PopupMenu.Large" parent="TextAppearance.Material.Menu" />
     <style name="TextAppearance.Material.Widget.PopupMenu.Small" parent="TextAppearance.Material.Menu" />
-    <style name="TextAppearance.Material.Widget.PopupMenu.Header" parent="TextAppearance.Material.Subhead">
+    <style name="TextAppearance.Material.Widget.PopupMenu.Header">
         <item name="fontFamily">@string/font_family_title_material</item>
         <item name="textSize">@dimen/text_size_menu_header_material</item>
+        <item name="textColor">?attr/textColorSecondary</item>
     </style>
 
     <style name="TextAppearance.Material.Widget.DropDownHint" parent="TextAppearance.Material.Menu" />
diff --git a/core/tests/coretests/src/android/net/LinkPropertiesTest.java b/core/tests/coretests/src/android/net/LinkPropertiesTest.java
index 075ceaa..b6b4f4f 100644
--- a/core/tests/coretests/src/android/net/LinkPropertiesTest.java
+++ b/core/tests/coretests/src/android/net/LinkPropertiesTest.java
@@ -48,6 +48,11 @@
     private static LinkAddress LINKADDRV6 = new LinkAddress(ADDRV6, 128);
     private static LinkAddress LINKADDRV6LINKLOCAL = new LinkAddress("fe80::1/64");
 
+    // TODO: replace all calls to NetworkUtils.numericToInetAddress with calls to this method.
+    private InetAddress Address(String addrString) {
+        return NetworkUtils.numericToInetAddress(addrString);
+    }
+
     public void assertLinkPropertiesEqual(LinkProperties source, LinkProperties target) {
         // Check implementation of equals(), element by element.
         assertTrue(source.isIdenticalInterfaceName(target));
@@ -647,5 +652,26 @@
         assertTrue(v6lp.isReachable(kLinkLocalDnsWithScope));
         assertTrue(v6lp.isReachable(kOnLinkDns));
         assertTrue(v6lp.isReachable(DNS6));
+
+        // Check isReachable on stacked links. This requires that the source IP address be assigned
+        // on the interface returned by the route lookup.
+        LinkProperties stacked = new LinkProperties();
+
+        // Can't add a stacked link without an interface name.
+        stacked.setInterfaceName("v4-test0");
+        v6lp.addStackedLink(stacked);
+
+        InetAddress stackedAddress = Address("192.0.0.4");
+        LinkAddress stackedLinkAddress = new LinkAddress(stackedAddress, 32);
+        assertFalse(v6lp.isReachable(stackedAddress));
+        stacked.addLinkAddress(stackedLinkAddress);
+        assertFalse(v6lp.isReachable(stackedAddress));
+        stacked.addRoute(new RouteInfo(stackedLinkAddress));
+        assertTrue(stacked.isReachable(stackedAddress));
+        assertTrue(v6lp.isReachable(stackedAddress));
+
+        assertFalse(v6lp.isReachable(DNS1));
+        stacked.addRoute(new RouteInfo((IpPrefix) null, stackedAddress));
+        assertTrue(v6lp.isReachable(DNS1));
     }
 }
diff --git a/core/tests/coretests/src/android/util/OrientationUtil.java b/core/tests/coretests/src/android/util/OrientationUtil.java
new file mode 100644
index 0000000..ecdca5d
--- /dev/null
+++ b/core/tests/coretests/src/android/util/OrientationUtil.java
@@ -0,0 +1,67 @@
+/*
+ * 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.util;
+
+import android.app.Activity;
+import android.app.Instrumentation;
+import android.content.pm.ActivityInfo;
+
+import android.test.ActivityInstrumentationTestCase2;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * Utilities for manipulating screen orientation.
+ */
+public final class OrientationUtil {
+
+    private final Activity mActivity;
+    private final Instrumentation mInstrumentation;
+
+    private final Runnable mSetToPortrait = new Runnable() {
+        @Override
+        public void run() {
+            mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
+        }
+    };
+
+    private final Runnable mSetToLandscape = new Runnable() {
+        @Override
+        public void run() {
+            mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
+        }
+    };
+
+    public static OrientationUtil initializeAndStartActivityIfNotStarted(
+            ActivityInstrumentationTestCase2 testCase) {
+        Preconditions.checkNotNull(testCase);
+        return new OrientationUtil(testCase.getActivity(), testCase.getInstrumentation());
+    }
+
+    private OrientationUtil(Activity activity, Instrumentation instrumentation) {
+        mActivity = activity;
+        mInstrumentation = instrumentation;
+    }
+
+    public void setPortraitOrientation() {
+        mInstrumentation.runOnMainSync(mSetToPortrait);
+    }
+
+    public void setLandscapeOrientation() {
+        mInstrumentation.runOnMainSync(mSetToLandscape);
+    }
+}
diff --git a/core/tests/coretests/src/android/widget/TextViewActivityTest.java b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
index 4db1d9a..6a76a27 100644
--- a/core/tests/coretests/src/android/widget/TextViewActivityTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
@@ -36,6 +36,7 @@
 
 import android.test.ActivityInstrumentationTestCase2;
 import android.test.suitebuilder.annotation.SmallTest;
+import android.util.OrientationUtil;
 import android.view.KeyEvent;
 
 /**
@@ -43,14 +44,20 @@
  */
 public class TextViewActivityTest extends ActivityInstrumentationTestCase2<TextViewActivity>{
 
+    private OrientationUtil mOrientationUtil;
+
     public TextViewActivityTest() {
         super(TextViewActivity.class);
     }
 
+    @Override
+    public void setUp() {
+        mOrientationUtil = OrientationUtil.initializeAndStartActivityIfNotStarted(this);
+        mOrientationUtil.setPortraitOrientation();
+    }
+
     @SmallTest
     public void testTypedTextIsOnScreen() throws Exception {
-        getActivity();
-
         final String helloWorld = "Hello world!";
         onView(withId(R.id.textview)).perform(click());
         onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(helloWorld));
@@ -60,8 +67,6 @@
 
     @SmallTest
     public void testPositionCursorAtTextAtIndex() throws Exception {
-        getActivity();
-
         final String helloWorld = "Hello world!";
         onView(withId(R.id.textview)).perform(click());
         onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(helloWorld));
@@ -74,8 +79,6 @@
 
     @SmallTest
     public void testLongPressToSelect() throws Exception {
-        getActivity();
-
         final String helloWorld = "Hello Kirk!";
         onView(withId(R.id.textview)).perform(click());
         onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(helloWorld));
@@ -87,8 +90,6 @@
 
     @SmallTest
     public void testLongPressEmptySpace() throws Exception {
-        getActivity();
-
         final String helloWorld = "Hello big round sun!";
         onView(withId(R.id.textview)).perform(click());
         onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(helloWorld));
@@ -102,8 +103,6 @@
 
     @SmallTest
     public void testLongPressAndDragToSelect() throws Exception {
-        getActivity();
-
         final String helloWorld = "Hello little handsome boy!";
         onView(withId(R.id.textview)).perform(click());
         onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(helloWorld));
@@ -115,8 +114,6 @@
 
     @SmallTest
     public void testDoubleTapToSelect() throws Exception {
-        getActivity();
-
         final String helloWorld = "Hello SuetYi!";
         onView(withId(R.id.textview)).perform(click());
         onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(helloWorld));
@@ -128,8 +125,6 @@
 
     @SmallTest
     public void testDoubleTapAndDragToSelect() throws Exception {
-        getActivity();
-
         final String helloWorld = "Hello young beautiful girl!";
         onView(withId(R.id.textview)).perform(click());
         onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(helloWorld));
@@ -141,8 +136,6 @@
 
     @SmallTest
     public void testSelectBackwordsByTouch() throws Exception {
-        getActivity();
-
         final String helloWorld = "Hello king of the Jungle!";
         onView(withId(R.id.textview)).perform(click());
         onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(helloWorld));
@@ -154,8 +147,6 @@
 
     @SmallTest
     public void testToolbarAppearsAfterSelection() throws Exception {
-        getActivity();
-
         // It'll be nice to check that the toolbar is not visible (or does not exist) here
         // I can't currently find a way to do this. I'll get to it later.
 
diff --git a/data/keyboards/Vendor_18d1_Product_5018.kcm b/data/keyboards/Vendor_18d1_Product_5018.kcm
new file mode 100644
index 0000000..0ca85a2
--- /dev/null
+++ b/data/keyboards/Vendor_18d1_Product_5018.kcm
@@ -0,0 +1,321 @@
+# 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.
+
+#
+# Key character map for Google Pixel C Keyboard
+#
+
+type FULL
+
+### Basic QWERTY keys ###
+
+key A {
+    label:                              'A'
+    base:                               'a'
+    shift, capslock:                    'A'
+}
+
+key B {
+    label:                              'B'
+    base:                               'b'
+    shift, capslock:                    'B'
+}
+
+key C {
+    label:                              'C'
+    base:                               'c'
+    shift, capslock:                    'C'
+    alt:                                '\u00e7'
+    shift+alt:                          '\u00c7'
+}
+
+key D {
+    label:                              'D'
+    base:                               'd'
+    shift, capslock:                    'D'
+}
+
+key E {
+    label:                              'E'
+    base:                               'e'
+    shift, capslock:                    'E'
+    alt:                                '\u0301'
+}
+
+key F {
+    label:                              'F'
+    base:                               'f'
+    shift, capslock:                    'F'
+}
+
+key G {
+    label:                              'G'
+    base:                               'g'
+    shift, capslock:                    'G'
+}
+
+key H {
+    label:                              'H'
+    base:                               'h'
+    shift, capslock:                    'H'
+}
+
+key I {
+    label:                              'I'
+    base:                               'i'
+    shift, capslock:                    'I'
+    alt:                                '\u0302'
+}
+
+key J {
+    label:                              'J'
+    base:                               'j'
+    shift, capslock:                    'J'
+}
+
+key K {
+    label:                              'K'
+    base:                               'k'
+    shift, capslock:                    'K'
+}
+
+key L {
+    label:                              'L'
+    base:                               'l'
+    shift, capslock:                    'L'
+}
+
+key M {
+    label:                              'M'
+    base:                               'm'
+    shift, capslock:                    'M'
+}
+
+key N {
+    label:                              'N'
+    base:                               'n'
+    shift, capslock:                    'N'
+    alt:                                '\u0303'
+}
+
+key O {
+    label:                              'O'
+    base:                               'o'
+    shift, capslock:                    'O'
+    ralt:                               '['
+    ralt+shift:                         '{'
+}
+
+key P {
+    label:                              'P'
+    base:                               'p'
+    shift, capslock:                    'P'
+    ralt:                               ']'
+    ralt+shift:                         '}'
+}
+
+key Q {
+    label:                              'Q'
+    base:                               'q'
+    shift, capslock:                    'Q'
+}
+
+key R {
+    label:                              'R'
+    base:                               'r'
+    shift, capslock:                    'R'
+}
+
+key S {
+    label:                              'S'
+    base:                               's'
+    shift, capslock:                    'S'
+    alt:                                '\u00df'
+}
+
+key T {
+    label:                              'T'
+    base:                               't'
+    shift, capslock:                    'T'
+}
+
+key U {
+    label:                              'U'
+    base:                               'u'
+    shift, capslock:                    'U'
+    alt:                                '\u0308'
+}
+
+key V {
+    label:                              'V'
+    base:                               'v'
+    shift, capslock:                    'V'
+}
+
+key W {
+    label:                              'W'
+    base:                               'w'
+    shift, capslock:                    'W'
+}
+
+key X {
+    label:                              'X'
+    base:                               'x'
+    shift, capslock:                    'X'
+}
+
+key Y {
+    label:                              'Y'
+    base:                               'y'
+    shift, capslock:                    'Y'
+}
+
+key Z {
+    label:                              'Z'
+    base:                               'z'
+    shift, capslock:                    'Z'
+}
+
+key 0 {
+    label:                              '0'
+    base:                               '0'
+    shift:                              ')'
+}
+
+key 1 {
+    label:                              '1'
+    base:                               '1'
+    shift:                              '!'
+    ralt:                               replace ESCAPE
+}
+
+key 2 {
+    label:                              '2'
+    base:                               '2'
+    shift:                              '@'
+    ralt:                               '`'
+    ralt+shift:                         '~'
+}
+
+key 3 {
+    label:                              '3'
+    base:                               '3'
+    shift:                              '#'
+}
+
+key 4 {
+    label:                              '4'
+    base:                               '4'
+    shift:                              '$'
+}
+
+key 5 {
+    label:                              '5'
+    base:                               '5'
+    shift:                              '%'
+}
+
+key 6 {
+    label:                              '6'
+    base:                               '6'
+    shift:                              '^'
+    alt+shift:                          '\u0302'
+}
+
+key 7 {
+    label:                              '7'
+    base:                               '7'
+    shift:                              '&'
+}
+
+key 8 {
+    label:                              '8'
+    base:                               '8'
+    shift:                              '*'
+}
+
+key 9 {
+    label:                              '9'
+    base:                               '9'
+    shift:                              '('
+}
+
+key SPACE {
+    label:                              ' '
+    base:                               ' '
+    alt, meta:                          fallback SEARCH
+    ctrl:                               fallback LANGUAGE_SWITCH
+}
+
+key ENTER {
+    label:                              '\n'
+    base:                               '\n'
+}
+
+key TAB {
+    label:                              '\t'
+    base:                               '\t'
+}
+
+key COMMA {
+    label:                              ','
+    base:                               ','
+    shift:                              '<'
+}
+
+key PERIOD {
+    label:                              '.'
+    base:                               '.'
+    shift:                              '>'
+}
+
+key SLASH {
+    label:                              '/'
+    base:                               '/'
+    shift:                              '?'
+}
+
+key MINUS {
+    label:                              '-'
+    base:                               '-'
+    shift:                              '_'
+}
+
+key EQUALS {
+    label:                              '='
+    base:                               '='
+    shift:                              '+'
+    ralt:                               '\\'
+    ralt+shift:                         '|'
+}
+
+key SEMICOLON {
+    label:                              ';'
+    base:                               ';'
+    shift:                              ':'
+}
+
+key APOSTROPHE {
+    label:                              '\''
+    base:                               '\''
+    shift:                              '"'
+}
+
+### Non-printing keys ###
+
+key ESCAPE {
+    base:                               fallback BACK
+    alt, meta:                          fallback HOME
+    ctrl:                               fallback MENU
+}
diff --git a/data/keyboards/Vendor_18d1_Product_5018.kl b/data/keyboards/Vendor_18d1_Product_5018.kl
new file mode 100644
index 0000000..e95ccb5
--- /dev/null
+++ b/data/keyboards/Vendor_18d1_Product_5018.kl
@@ -0,0 +1,84 @@
+# 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.
+
+#
+# Key layout for Google Pixel C Keyboard
+#
+
+# Row 1
+key 2     1
+key 3     2
+key 4     3
+key 5     4
+key 6     5
+key 7     6
+key 8     7
+key 9     8
+key 10    9
+key 11    0
+key 12    MINUS
+key 14    DEL         # Backspace
+
+# Row 2
+key 15    TAB
+key 16    Q
+key 17    W
+key 18    E
+key 19    R
+key 20    T
+key 21    Y
+key 22    U
+key 23    I
+key 24    O
+key 25    P
+key 13    EQUALS
+key 28    ENTER
+
+# Row 3
+key 125   META_LEFT   # "Search key"
+key 30    A
+key 31    S
+key 32    D
+key 33    F
+key 34    G
+key 35    H
+key 36    J
+key 37    K
+key 38    L
+key 39    SEMICOLON
+key 40    APOSTROPHE
+
+# Row 4
+key 42    SHIFT_LEFT
+key 44    Z
+key 45    X
+key 46    C
+key 47    V
+key 48    B
+key 49    N
+key 50    M
+key 51    COMMA
+key 52    PERIOD
+key 53    SLASH
+key 54    SHIFT_RIGHT
+
+# Row 5
+key 29    CTRL_LEFT
+key 56    ALT_LEFT
+key 57    SPACE
+key 100   ALT_RIGHT
+key 103   DPAD_UP
+key 105   DPAD_LEFT
+key 106   DPAD_RIGHT
+key 108   DPAD_DOWN
diff --git a/docs/html/guide/topics/resources/providing-resources.jd b/docs/html/guide/topics/resources/providing-resources.jd
index 07ca45a..c179a2e 100644
--- a/docs/html/guide/topics/resources/providing-resources.jd
+++ b/docs/html/guide/topics/resources/providing-resources.jd
@@ -646,7 +646,8 @@
         <code>xxhdpi</code><br/>
         <code>xxxhdpi</code><br/>
         <code>nodpi</code><br/>
-        <code>tvdpi</code>
+        <code>tvdpi</code><br/>
+        <code>anydpi</code>
       </td>
       <td>
         <ul class="nolist">
@@ -667,7 +668,11 @@
           <li>{@code tvdpi}: Screens somewhere between mdpi and hdpi; approximately 213dpi. This is
 not considered a "primary" density group. It is mostly intended for televisions and most
 apps shouldn't need it&mdash;providing mdpi and hdpi resources is sufficient for most apps and
-the system will scale them as appropriate. This qualifier was introduced with API level 13.</li>
+the system will scale them as appropriate. <em>Added in API Level 13</em></li>
+          <li>{@code anydpi}: This qualifier matches all screen densities and takes precedence over
+other qualifiers. This is useful for
+<a href="{@docRoot}training/material/drawables.html#VectorDrawables">vector drawables</a>.
+<em>Added in API Level 21</em></li>
         </ul>
         <p>There is a 3:4:6:8:12:16 scaling ratio between the six primary densities (ignoring the
 tvdpi density). So, a 9x9 bitmap in ldpi is 12x12 in mdpi, 18x18 in hdpi, 24x24 in xhdpi and so on.
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index 58de87a..11b4a9e 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -430,7 +430,7 @@
      * @param flags initial flag bits, as if they were passed via setFlags().
      */
     public Paint(int flags) {
-        mNativePaint = native_init();
+        mNativePaint = nInit();
         setFlags(flags | HIDDEN_DEFAULT_PAINT_FLAGS);
         // TODO: Turning off hinting has undesirable side effects, we need to
         //       revisit hinting once we add support for subpixel positioning
@@ -448,13 +448,14 @@
      *              new paint.
      */
     public Paint(Paint paint) {
-        mNativePaint = native_initWithPaint(paint.getNativeInstance());
+        mNativePaint = nInitWithPaint(paint.getNativeInstance());
         setClassVariablesFrom(paint);
     }
 
     /** Restores the paint to its default settings. */
     public void reset() {
-        native_reset(mNativePaint);
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        nReset(mNativePaint);
         setFlags(HIDDEN_DEFAULT_PAINT_FLAGS);
 
         // TODO: Turning off hinting has undesirable side effects, we need to
@@ -488,9 +489,11 @@
      * methods on this.
      */
     public void set(Paint src) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        if (src.mNativePaint == 0) throw new NullPointerException("Source is already finalized!");
         if (this != src) {
             // copy over the native settings
-            native_set(mNativePaint, src.mNativePaint);
+            nSet(mNativePaint, src.mNativePaint);
             setClassVariablesFrom(src);
         }
     }
@@ -538,10 +541,11 @@
      * @hide
      */
     public long getNativeInstance() {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         long newNativeShader = mShader == null ? 0 : mShader.getNativeInstance();
         if (newNativeShader != mNativeShader) {
             mNativeShader = newNativeShader;
-            native_setShader(mNativePaint, mNativeShader);
+            nSetShader(mNativePaint, mNativeShader);
         }
         return mNativePaint;
     }
@@ -574,26 +578,46 @@
      *
      * @return the paint's flags (see enums ending in _Flag for bit masks)
      */
-    public native int getFlags();
+    public int getFlags() {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        return nGetFlags(mNativePaint);
+    }
+
+    private native int nGetFlags(long paintPtr);
 
     /**
      * Set the paint's flags. Use the Flag enum to specific flag values.
      *
      * @param flags The new flag bits for the paint
      */
-    public native void setFlags(int flags);
+    public void setFlags(int flags) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        nSetFlags(mNativePaint, flags);
+    }
+
+    private native void nSetFlags(long paintPtr, int flags);
 
     /**
      * Return the paint's hinting mode.  Returns either
      * {@link #HINTING_OFF} or {@link #HINTING_ON}.
      */
-    public native int getHinting();
+    public int getHinting() {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        return nGetHinting(mNativePaint);
+    }
+
+    private native int nGetHinting(long paintPtr);
 
     /**
      * Set the paint's hinting mode.  May be either
      * {@link #HINTING_OFF} or {@link #HINTING_ON}.
      */
-    public native void setHinting(int mode);
+    public void setHinting(int mode) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        nSetHinting(mNativePaint, mode);
+    }
+
+    private native void nSetHinting(long paintPtr, int mode);
 
     /**
      * Helper for getFlags(), returning true if ANTI_ALIAS_FLAG bit is set
@@ -615,7 +639,12 @@
      *
      * @param aa true to set the antialias bit in the flags, false to clear it
      */
-    public native void setAntiAlias(boolean aa);
+    public void setAntiAlias(boolean aa) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        nSetAntiAlias(mNativePaint, aa);
+    }
+
+    private native void nSetAntiAlias(long paintPtr, boolean aa);
 
     /**
      * Helper for getFlags(), returning true if DITHER_FLAG bit is set
@@ -641,7 +670,12 @@
      *
      * @param dither true to set the dithering bit in flags, false to clear it
      */
-    public native void setDither(boolean dither);
+    public void setDither(boolean dither) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        nSetDither(mNativePaint, dither);
+    }
+
+    private native void nSetDither(long paintPtr, boolean dither);
 
     /**
      * Helper for getFlags(), returning true if LINEAR_TEXT_FLAG bit is set
@@ -658,7 +692,12 @@
      * @param linearText true to set the linearText bit in the paint's flags,
      *                   false to clear it.
      */
-    public native void setLinearText(boolean linearText);
+    public void setLinearText(boolean linearText) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        nSetLinearText(mNativePaint, linearText);
+    }
+
+    private native void nSetLinearText(long paintPtr, boolean linearText);
 
     /**
      * Helper for getFlags(), returning true if SUBPIXEL_TEXT_FLAG bit is set
@@ -675,7 +714,12 @@
      * @param subpixelText true to set the subpixelText bit in the paint's
      *                     flags, false to clear it.
      */
-    public native void setSubpixelText(boolean subpixelText);
+    public void setSubpixelText(boolean subpixelText) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        nSetSubpixelText(mNativePaint, subpixelText);
+    }
+
+    private native void nSetSubpixelText(long paintPtr, boolean subpixelText);
 
     /**
      * Helper for getFlags(), returning true if UNDERLINE_TEXT_FLAG bit is set
@@ -692,7 +736,12 @@
      * @param underlineText true to set the underlineText bit in the paint's
      *                      flags, false to clear it.
      */
-    public native void setUnderlineText(boolean underlineText);
+    public void setUnderlineText(boolean underlineText) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        nSetUnderlineText(mNativePaint, underlineText);
+    }
+
+    private native void nSetUnderlineText(long paintPtr, boolean underlineText);
 
     /**
      * Helper for getFlags(), returning true if STRIKE_THRU_TEXT_FLAG bit is set
@@ -709,7 +758,12 @@
      * @param strikeThruText true to set the strikeThruText bit in the paint's
      *                       flags, false to clear it.
      */
-    public native void setStrikeThruText(boolean strikeThruText);
+    public void setStrikeThruText(boolean strikeThruText) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        nSetStrikeThruText(mNativePaint, strikeThruText);
+    }
+
+    private native void nSetStrikeThruText(long paintPtr, boolean strikeThruText);
 
     /**
      * Helper for getFlags(), returning true if FAKE_BOLD_TEXT_FLAG bit is set
@@ -726,7 +780,12 @@
      * @param fakeBoldText true to set the fakeBoldText bit in the paint's
      *                     flags, false to clear it.
      */
-    public native void setFakeBoldText(boolean fakeBoldText);
+    public void setFakeBoldText(boolean fakeBoldText) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        nSetFakeBoldText(mNativePaint, fakeBoldText);
+    }
+
+    private native void nSetFakeBoldText(long paintPtr, boolean fakeBoldText);
 
     /**
      * Whether or not the bitmap filter is activated.
@@ -749,7 +808,12 @@
      * @param filter true to set the FILTER_BITMAP_FLAG bit in the paint's
      *               flags, false to clear it.
      */
-    public native void setFilterBitmap(boolean filter);
+    public void setFilterBitmap(boolean filter) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        nSetFilterBitmap(mNativePaint, filter);
+    }
+
+    private native void nSetFilterBitmap(long paintPtr, boolean filter);
 
     /**
      * Return the paint's style, used for controlling how primitives'
@@ -759,7 +823,8 @@
      * @return the paint's style setting (Fill, Stroke, StrokeAndFill)
      */
     public Style getStyle() {
-        return sStyleArray[native_getStyle(mNativePaint)];
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        return sStyleArray[nGetStyle(mNativePaint)];
     }
 
     /**
@@ -770,7 +835,8 @@
      * @param style The new style to set in the paint
      */
     public void setStyle(Style style) {
-        native_setStyle(mNativePaint, style.nativeInt);
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        nSetStyle(mNativePaint, style.nativeInt);
     }
 
     /**
@@ -782,7 +848,12 @@
      * @return the paint's color (and alpha).
      */
     @ColorInt
-    public native int getColor();
+    public int getColor() {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        return nGetColor(mNativePaint);
+    }
+
+    private native int nGetColor(long paintPtr);
 
     /**
      * Set the paint's color. Note that the color is an int containing alpha
@@ -792,7 +863,12 @@
      *
      * @param color The new color (including alpha) to set in the paint.
      */
-    public native void setColor(@ColorInt int color);
+    public void setColor(@ColorInt int color) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        nSetColor(mNativePaint, color);
+    }
+
+    private native void nSetColor(long paintPtr, @ColorInt int color);
 
     /**
      * Helper to getColor() that just returns the color's alpha value. This is
@@ -801,7 +877,12 @@
      *
      * @return the alpha component of the paint's color.
      */
-    public native int getAlpha();
+    public int getAlpha() {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        return nGetAlpha(mNativePaint);
+    }
+
+    private native int nGetAlpha(long paintPtr);
 
     /**
      * Helper to setColor(), that only assigns the color's alpha value,
@@ -810,7 +891,12 @@
      *
      * @param a set the alpha component [0..255] of the paint's color.
      */
-    public native void setAlpha(int a);
+    public void setAlpha(int a) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        nSetAlpha(mNativePaint, a);
+    }
+
+    private native void nSetAlpha(long paintPtr, int a);
 
     /**
      * Helper to setColor(), that takes a,r,g,b and constructs the color int
@@ -833,7 +919,12 @@
      * @return the paint's stroke width, used whenever the paint's style is
      *         Stroke or StrokeAndFill.
      */
-    public native float getStrokeWidth();
+    public float getStrokeWidth() {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        return nGetStrokeWidth(mNativePaint);
+    }
+
+    private native float nGetStrokeWidth(long paintPtr);
 
     /**
      * Set the width for stroking.
@@ -843,7 +934,12 @@
      * @param width set the paint's stroke width, used whenever the paint's
      *              style is Stroke or StrokeAndFill.
      */
-    public native void setStrokeWidth(float width);
+    public void setStrokeWidth(float width) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        nSetStrokeWidth(mNativePaint, width);
+    }
+
+    private native void nSetStrokeWidth(long paintPtr, float width);
 
     /**
      * Return the paint's stroke miter value. Used to control the behavior
@@ -852,7 +948,12 @@
      * @return the paint's miter limit, used whenever the paint's style is
      *         Stroke or StrokeAndFill.
      */
-    public native float getStrokeMiter();
+    public float getStrokeMiter() {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        return nGetStrokeMiter(mNativePaint);
+    }
+
+    private native float nGetStrokeMiter(long paintPtr);
 
     /**
      * Set the paint's stroke miter value. This is used to control the behavior
@@ -861,7 +962,12 @@
      * @param miter set the miter limit on the paint, used whenever the paint's
      *              style is Stroke or StrokeAndFill.
      */
-    public native void setStrokeMiter(float miter);
+    public void setStrokeMiter(float miter) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        nSetStrokeMiter(mNativePaint, miter);
+    }
+
+    private native void nSetStrokeMiter(long paintPtr, float miter);
 
     /**
      * Return the paint's Cap, controlling how the start and end of stroked
@@ -871,7 +977,8 @@
      *         style is Stroke or StrokeAndFill.
      */
     public Cap getStrokeCap() {
-        return sCapArray[native_getStrokeCap(mNativePaint)];
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        return sCapArray[nGetStrokeCap(mNativePaint)];
     }
 
     /**
@@ -881,7 +988,8 @@
      *            style is Stroke or StrokeAndFill.
      */
     public void setStrokeCap(Cap cap) {
-        native_setStrokeCap(mNativePaint, cap.nativeInt);
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        nSetStrokeCap(mNativePaint, cap.nativeInt);
     }
 
     /**
@@ -890,7 +998,8 @@
      * @return the paint's Join.
      */
     public Join getStrokeJoin() {
-        return sJoinArray[native_getStrokeJoin(mNativePaint)];
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        return sJoinArray[nGetStrokeJoin(mNativePaint)];
     }
 
     /**
@@ -900,7 +1009,8 @@
      *             Stroke or StrokeAndFill.
      */
     public void setStrokeJoin(Join join) {
-        native_setStrokeJoin(mNativePaint, join.nativeInt);
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        nSetStrokeJoin(mNativePaint, join.nativeInt);
     }
 
     /**
@@ -915,7 +1025,8 @@
      *                 drawn with a hairline (width == 0)
      */
     public boolean getFillPath(Path src, Path dst) {
-        return native_getFillPath(mNativePaint, src.ni(), dst.ni());
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        return nGetFillPath(mNativePaint, src.ni(), dst.ni());
     }
 
     /**
@@ -958,10 +1069,11 @@
      * @return       filter
      */
     public ColorFilter setColorFilter(ColorFilter filter) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         long filterNative = 0;
         if (filter != null)
             filterNative = filter.native_instance;
-        native_setColorFilter(mNativePaint, filterNative);
+        nSetColorFilter(mNativePaint, filterNative);
         mColorFilter = filter;
         return filter;
     }
@@ -985,10 +1097,11 @@
      * @return         xfermode
      */
     public Xfermode setXfermode(Xfermode xfermode) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         long xfermodeNative = 0;
         if (xfermode != null)
             xfermodeNative = xfermode.native_instance;
-        native_setXfermode(mNativePaint, xfermodeNative);
+        nSetXfermode(mNativePaint, xfermodeNative);
         mXfermode = xfermode;
         return xfermode;
     }
@@ -1012,11 +1125,12 @@
      * @return       effect
      */
     public PathEffect setPathEffect(PathEffect effect) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         long effectNative = 0;
         if (effect != null) {
             effectNative = effect.native_instance;
         }
-        native_setPathEffect(mNativePaint, effectNative);
+        nSetPathEffect(mNativePaint, effectNative);
         mPathEffect = effect;
         return effect;
     }
@@ -1041,11 +1155,12 @@
      * @return           maskfilter
      */
     public MaskFilter setMaskFilter(MaskFilter maskfilter) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         long maskfilterNative = 0;
         if (maskfilter != null) {
             maskfilterNative = maskfilter.native_instance;
         }
-        native_setMaskFilter(mNativePaint, maskfilterNative);
+        nSetMaskFilter(mNativePaint, maskfilterNative);
         mMaskFilter = maskfilter;
         return maskfilter;
     }
@@ -1072,11 +1187,12 @@
      * @return         typeface
      */
     public Typeface setTypeface(Typeface typeface) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         long typefaceNative = 0;
         if (typeface != null) {
             typefaceNative = typeface.native_instance;
         }
-        native_setTypeface(mNativePaint, typefaceNative);
+        nSetTypeface(mNativePaint, typefaceNative);
         mTypeface = typeface;
         mNativeTypeface = typefaceNative;
         return typeface;
@@ -1110,11 +1226,12 @@
      */
     @Deprecated
     public Rasterizer setRasterizer(Rasterizer rasterizer) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         long rasterizerNative = 0;
         if (rasterizer != null) {
             rasterizerNative = rasterizer.native_instance;
         }
-        native_setRasterizer(mNativePaint, rasterizerNative);
+        nSetRasterizer(mNativePaint, rasterizerNative);
         mRasterizer = rasterizer;
         return rasterizer;
     }
@@ -1132,7 +1249,8 @@
      * opaque, or the alpha from the shadow color if not.
      */
     public void setShadowLayer(float radius, float dx, float dy, int shadowColor) {
-      native_setShadowLayer(mNativePaint, radius, dx, dy, shadowColor);
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+      nSetShadowLayer(mNativePaint, radius, dx, dy, shadowColor);
     }
 
     /**
@@ -1149,7 +1267,8 @@
      * @hide
      */
     public boolean hasShadowLayer() {
-      return native_hasShadowLayer(mNativePaint);
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        return nHasShadowLayer(mNativePaint);
     }
 
     /**
@@ -1161,7 +1280,8 @@
      * @return the paint's Align value for drawing text.
      */
     public Align getTextAlign() {
-        return sAlignArray[native_getTextAlign(mNativePaint)];
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        return sAlignArray[nGetTextAlign(mNativePaint)];
     }
 
     /**
@@ -1173,7 +1293,8 @@
      * @param align set the paint's Align value for drawing text.
      */
     public void setTextAlign(Align align) {
-        native_setTextAlign(mNativePaint, align.nativeInt);
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        nSetTextAlign(mNativePaint, align.nativeInt);
     }
 
     /**
@@ -1206,6 +1327,7 @@
      * @param locale the paint's locale value for drawing text, must not be null.
      */
     public void setTextLocale(@NonNull Locale locale) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         if (locale == null) {
             throw new IllegalArgumentException("locale cannot be null");
         }
@@ -1213,7 +1335,7 @@
             return;
         }
         mLocales = new LocaleList(locale);
-        native_setTextLocale(mNativePaint, locale.toString());
+        nSetTextLocale(mNativePaint, locale.toString());
     }
 
     /**
@@ -1244,13 +1366,14 @@
      * @param locales the paint's locale list for drawing text, must not be null or empty.
      */
     public void setTextLocales(@NonNull @Size(min=1) LocaleList locales) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         if (locales == null || locales.isEmpty()) {
             throw new IllegalArgumentException("locales cannot be null or empty");
         }
         if (locales.equals(mLocales)) return;
         mLocales = locales;
         // TODO: Pass the whole LocaleList to native code
-        native_setTextLocale(mNativePaint, locales.getPrimary().toString());
+        nSetTextLocale(mNativePaint, locales.getPrimary().toString());
     }
 
     /**
@@ -1258,7 +1381,12 @@
      *
      * @return true if elegant metrics are enabled for text drawing.
      */
-    public native boolean isElegantTextHeight();
+    public boolean isElegantTextHeight() {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        return nIsElegantTextHeight(mNativePaint);
+    }
+
+    private native boolean nIsElegantTextHeight(long paintPtr);
 
     /**
      * Set the paint's elegant height metrics flag. This setting selects font
@@ -1267,21 +1395,36 @@
      *
      * @param elegant set the paint's elegant metrics flag for drawing text.
      */
-    public native void setElegantTextHeight(boolean elegant);
+    public void setElegantTextHeight(boolean elegant) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        nSetElegantTextHeight(mNativePaint, elegant);
+    }
+
+    private native void nSetElegantTextHeight(long paintPtr, boolean elegant);
 
     /**
      * Return the paint's text size.
      *
      * @return the paint's text size.
      */
-    public native float getTextSize();
+    public float getTextSize() {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        return nGetTextSize(mNativePaint);
+    }
+
+    private native float nGetTextSize(long paintPtr);
 
     /**
      * Set the paint's text size. This value must be > 0
      *
      * @param textSize set the paint's text size.
      */
-    public native void setTextSize(float textSize);
+    public void setTextSize(float textSize) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        nSetTextSize(mNativePaint, textSize);
+    }
+
+    private native void nSetTextSize(long paintPtr, float textSize);
 
     /**
      * Return the paint's horizontal scale factor for text. The default value
@@ -1289,7 +1432,12 @@
      *
      * @return the paint's scale factor in X for drawing/measuring text
      */
-    public native float getTextScaleX();
+    public float getTextScaleX() {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        return nGetTextScaleX(mNativePaint);
+    }
+
+    private native float nGetTextScaleX(long paintPtr);
 
     /**
      * Set the paint's horizontal scale factor for text. The default value
@@ -1298,7 +1446,12 @@
      *
      * @param scaleX set the paint's scale in X for drawing/measuring text.
      */
-    public native void setTextScaleX(float scaleX);
+    public void setTextScaleX(float scaleX) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        nSetTextScaleX(mNativePaint, scaleX);
+    }
+
+    private native void nSetTextScaleX(long paintPtr, float scaleX);
 
     /**
      * Return the paint's horizontal skew factor for text. The default value
@@ -1306,7 +1459,12 @@
      *
      * @return         the paint's skew factor in X for drawing text.
      */
-    public native float getTextSkewX();
+    public float getTextSkewX() {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        return nGetTextSkewX(mNativePaint);
+    }
+
+    private native float nGetTextSkewX(long paintPtr);
 
     /**
      * Set the paint's horizontal skew factor for text. The default value
@@ -1314,7 +1472,12 @@
      *
      * @param skewX set the paint's skew factor in X for drawing text.
      */
-    public native void setTextSkewX(float skewX);
+    public void setTextSkewX(float skewX) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        nSetTextSkewX(mNativePaint, skewX);
+    }
+
+    private native void nSetTextSkewX(long paintPtr, float skewX);
 
     /**
      * Return the paint's letter-spacing for text. The default value
@@ -1323,7 +1486,8 @@
      * @return         the paint's letter-spacing for drawing text.
      */
     public float getLetterSpacing() {
-        return native_getLetterSpacing(mNativePaint);
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        return nGetLetterSpacing(mNativePaint);
     }
 
     /**
@@ -1334,7 +1498,8 @@
      * @param letterSpacing set the paint's letter-spacing for drawing text.
      */
     public void setLetterSpacing(float letterSpacing) {
-        native_setLetterSpacing(mNativePaint, letterSpacing);
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        nSetLetterSpacing(mNativePaint, letterSpacing);
     }
 
     /**
@@ -1355,6 +1520,7 @@
      * @param settings the font feature settings string to use, may be null.
      */
     public void setFontFeatureSettings(String settings) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         if (settings != null && settings.equals("")) {
             settings = null;
         }
@@ -1363,7 +1529,7 @@
             return;
         }
         mFontFeatureSettings = settings;
-        native_setFontFeatureSettings(mNativePaint, settings);
+        nSetFontFeatureSettings(mNativePaint, settings);
     }
 
     /**
@@ -1374,7 +1540,8 @@
      * @hide
      */
     public int getHyphenEdit() {
-        return native_getHyphenEdit(mNativePaint);
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        return nGetHyphenEdit(mNativePaint);
     }
 
     /**
@@ -1386,7 +1553,8 @@
      * @hide
      */
     public void setHyphenEdit(int hyphen) {
-        native_setHyphenEdit(mNativePaint, hyphen);
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        nSetHyphenEdit(mNativePaint, hyphen);
     }
 
     /**
@@ -1396,7 +1564,12 @@
      * @return the distance above (negative) the baseline (ascent) based on the
      *         current typeface and text size.
      */
-    public native float ascent();
+    public float ascent() {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        return nAscent(mNativePaint, mNativeTypeface);
+    }
+
+    private native float nAscent(long paintPtr, long typefacePtr);
 
     /**
      * Return the distance below (positive) the baseline (descent) based on the
@@ -1405,7 +1578,12 @@
      * @return the distance below (positive) the baseline (descent) based on
      *         the current typeface and text size.
      */
-    public native float descent();
+    public float descent() {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        return nDescent(mNativePaint, mNativeTypeface);
+    }
+
+    private native float nDescent(long paintPtr, long typefacePtr);
 
     /**
      * Class that describes the various metrics for a font at a given text size.
@@ -1447,7 +1625,13 @@
      *                the appropriate values given the paint's text attributes.
      * @return the font's recommended interline spacing.
      */
-    public native float getFontMetrics(FontMetrics metrics);
+    public float getFontMetrics(FontMetrics metrics) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        return nGetFontMetrics(mNativePaint, mNativeTypeface, metrics);
+    }
+
+    private native float nGetFontMetrics(long paintPtr,
+            long typefacePtr, FontMetrics metrics);
 
     /**
      * Allocates a new FontMetrics object, and then calls getFontMetrics(fm)
@@ -1487,7 +1671,13 @@
      *
      * @return the font's interline spacing.
      */
-    public native int getFontMetricsInt(FontMetricsInt fmi);
+    public int getFontMetricsInt(FontMetricsInt fmi) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        return nGetFontMetricsInt(mNativePaint, mNativeTypeface, fmi);
+    }
+
+    private native int nGetFontMetricsInt(long paintPtr,
+            long typefacePtr, FontMetricsInt fmi);
 
     public FontMetricsInt getFontMetricsInt() {
         FontMetricsInt fm = new FontMetricsInt();
@@ -1515,6 +1705,7 @@
      * @return      The width of the text
      */
     public float measureText(char[] text, int index, int count) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         if (text == null) {
             throw new IllegalArgumentException("text cannot be null");
         }
@@ -1526,13 +1717,13 @@
             return 0f;
         }
         if (!mHasCompatScaling) {
-            return (float) Math.ceil(native_getTextAdvances(mNativePaint, mNativeTypeface, text,
+            return (float) Math.ceil(nGetTextAdvances(mNativePaint, mNativeTypeface, text,
                     index, count, index, count, mBidiFlags, null, 0));
         }
 
         final float oldSize = getTextSize();
         setTextSize(oldSize * mCompatScaling);
-        float w = native_getTextAdvances(mNativePaint, mNativeTypeface, text, index, count, index,
+        float w = nGetTextAdvances(mNativePaint, mNativeTypeface, text, index, count, index,
                 count, mBidiFlags, null, 0);
         setTextSize(oldSize);
         return (float) Math.ceil(w*mInvCompatScaling);
@@ -1547,6 +1738,7 @@
      * @return      The width of the text
      */
     public float measureText(String text, int start, int end) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         if (text == null) {
             throw new IllegalArgumentException("text cannot be null");
         }
@@ -1558,12 +1750,12 @@
             return 0f;
         }
         if (!mHasCompatScaling) {
-            return (float) Math.ceil(native_getTextAdvances(mNativePaint, mNativeTypeface, text,
+            return (float) Math.ceil(nGetTextAdvances(mNativePaint, mNativeTypeface, text,
                     start, end, start, end, mBidiFlags, null, 0));
         }
         final float oldSize = getTextSize();
         setTextSize(oldSize * mCompatScaling);
-        float w = native_getTextAdvances(mNativePaint, mNativeTypeface, text, start, end, start,
+        float w = nGetTextAdvances(mNativePaint, mNativeTypeface, text, start, end, start,
                 end, mBidiFlags, null, 0);
         setTextSize(oldSize);
         return (float) Math.ceil(w * mInvCompatScaling);
@@ -1576,6 +1768,7 @@
      * @return      The width of the text
      */
     public float measureText(String text) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         if (text == null) {
             throw new IllegalArgumentException("text cannot be null");
         }
@@ -1636,6 +1829,7 @@
      */
     public int breakText(char[] text, int index, int count,
                                 float maxWidth, float[] measuredWidth) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         if (text == null) {
             throw new IllegalArgumentException("text cannot be null");
         }
@@ -1647,20 +1841,20 @@
             return 0;
         }
         if (!mHasCompatScaling) {
-            return native_breakText(mNativePaint, mNativeTypeface, text, index, count, maxWidth,
+            return nBreakText(mNativePaint, mNativeTypeface, text, index, count, maxWidth,
                     mBidiFlags, measuredWidth);
         }
 
         final float oldSize = getTextSize();
         setTextSize(oldSize * mCompatScaling);
-        int res = native_breakText(mNativePaint, mNativeTypeface, text, index, count,
+        int res = nBreakText(mNativePaint, mNativeTypeface, text, index, count,
                 maxWidth * mCompatScaling, mBidiFlags, measuredWidth);
         setTextSize(oldSize);
         if (measuredWidth != null) measuredWidth[0] *= mInvCompatScaling;
         return res;
     }
 
-    private static native int native_breakText(long native_object, long native_typeface,
+    private static native int nBreakText(long nObject, long nTypeface,
                                                char[] text, int index, int count,
                                                float maxWidth, int bidiFlags, float[] measuredWidth);
 
@@ -1683,6 +1877,7 @@
     public int breakText(CharSequence text, int start, int end,
                          boolean measureForwards,
                          float maxWidth, float[] measuredWidth) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         if (text == null) {
             throw new IllegalArgumentException("text cannot be null");
         }
@@ -1731,6 +1926,7 @@
      */
     public int breakText(String text, boolean measureForwards,
                                 float maxWidth, float[] measuredWidth) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         if (text == null) {
             throw new IllegalArgumentException("text cannot be null");
         }
@@ -1739,20 +1935,20 @@
             return 0;
         }
         if (!mHasCompatScaling) {
-            return native_breakText(mNativePaint, mNativeTypeface, text, measureForwards,
+            return nBreakText(mNativePaint, mNativeTypeface, text, measureForwards,
                     maxWidth, mBidiFlags, measuredWidth);
         }
 
         final float oldSize = getTextSize();
         setTextSize(oldSize*mCompatScaling);
-        int res = native_breakText(mNativePaint, mNativeTypeface, text, measureForwards,
+        int res = nBreakText(mNativePaint, mNativeTypeface, text, measureForwards,
                 maxWidth*mCompatScaling, mBidiFlags, measuredWidth);
         setTextSize(oldSize);
         if (measuredWidth != null) measuredWidth[0] *= mInvCompatScaling;
         return res;
     }
 
-    private static native int native_breakText(long native_object, long native_typeface,
+    private static native int nBreakText(long nObject, long nTypeface,
                                         String text, boolean measureForwards,
                                         float maxWidth, int bidiFlags, float[] measuredWidth);
 
@@ -1768,6 +1964,7 @@
      */
     public int getTextWidths(char[] text, int index, int count,
                              float[] widths) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         if (text == null) {
             throw new IllegalArgumentException("text cannot be null");
         }
@@ -1780,14 +1977,14 @@
             return 0;
         }
         if (!mHasCompatScaling) {
-            native_getTextAdvances(mNativePaint, mNativeTypeface, text, index, count, index, count,
+            nGetTextAdvances(mNativePaint, mNativeTypeface, text, index, count, index, count,
                     mBidiFlags, widths, 0);
             return count;
         }
 
         final float oldSize = getTextSize();
         setTextSize(oldSize * mCompatScaling);
-        native_getTextAdvances(mNativePaint, mNativeTypeface, text, index, count, index, count,
+        nGetTextAdvances(mNativePaint, mNativeTypeface, text, index, count, index, count,
                 mBidiFlags, widths, 0);
         setTextSize(oldSize);
         for (int i = 0; i < count; i++) {
@@ -1851,6 +2048,7 @@
      * @return       the number of code units in the specified text.
      */
     public int getTextWidths(String text, int start, int end, float[] widths) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         if (text == null) {
             throw new IllegalArgumentException("text cannot be null");
         }
@@ -1865,14 +2063,14 @@
             return 0;
         }
         if (!mHasCompatScaling) {
-            native_getTextAdvances(mNativePaint, mNativeTypeface, text, start, end, start, end,
+            nGetTextAdvances(mNativePaint, mNativeTypeface, text, start, end, start, end,
                     mBidiFlags, widths, 0);
             return end - start;
         }
 
         final float oldSize = getTextSize();
         setTextSize(oldSize * mCompatScaling);
-        native_getTextAdvances(mNativePaint, mNativeTypeface, text, start, end, start, end,
+        nGetTextAdvances(mNativePaint, mNativeTypeface, text, start, end, start, end,
                 mBidiFlags, widths, 0);
         setTextSize(oldSize);
         for (int i = 0; i < end - start; i++) {
@@ -1904,6 +2102,7 @@
             int contextIndex, int contextCount, boolean isRtl, float[] advances,
             int advancesIndex) {
 
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         if (chars == null) {
             throw new IllegalArgumentException("text cannot be null");
         }
@@ -1920,14 +2119,14 @@
             return 0f;
         }
         if (!mHasCompatScaling) {
-            return native_getTextAdvances(mNativePaint, mNativeTypeface, chars, index, count,
+            return nGetTextAdvances(mNativePaint, mNativeTypeface, chars, index, count,
                     contextIndex, contextCount, isRtl ? BIDI_FORCE_RTL : BIDI_FORCE_LTR, advances,
                     advancesIndex);
         }
 
         final float oldSize = getTextSize();
         setTextSize(oldSize * mCompatScaling);
-        float res = native_getTextAdvances(mNativePaint, mNativeTypeface, chars, index, count,
+        float res = nGetTextAdvances(mNativePaint, mNativeTypeface, chars, index, count,
                 contextIndex, contextCount, isRtl ? BIDI_FORCE_RTL : BIDI_FORCE_LTR, advances,
                 advancesIndex);
         setTextSize(oldSize);
@@ -1950,7 +2149,7 @@
     public float getTextRunAdvances(CharSequence text, int start, int end,
             int contextStart, int contextEnd, boolean isRtl, float[] advances,
             int advancesIndex) {
-
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         if (text == null) {
             throw new IllegalArgumentException("text cannot be null");
         }
@@ -2032,6 +2231,7 @@
      */
     public float getTextRunAdvances(String text, int start, int end, int contextStart,
             int contextEnd, boolean isRtl, float[] advances, int advancesIndex) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         if (text == null) {
             throw new IllegalArgumentException("text cannot be null");
         }
@@ -2048,14 +2248,14 @@
         }
 
         if (!mHasCompatScaling) {
-            return native_getTextAdvances(mNativePaint, mNativeTypeface, text, start, end,
+            return nGetTextAdvances(mNativePaint, mNativeTypeface, text, start, end,
                     contextStart, contextEnd, isRtl ? BIDI_FORCE_RTL : BIDI_FORCE_LTR, advances,
                     advancesIndex);
         }
 
         final float oldSize = getTextSize();
         setTextSize(oldSize * mCompatScaling);
-        float totalAdvance = native_getTextAdvances(mNativePaint, mNativeTypeface, text, start,
+        float totalAdvance = nGetTextAdvances(mNativePaint, mNativeTypeface, text, start,
                 end, contextStart, contextEnd, isRtl ? BIDI_FORCE_RTL : BIDI_FORCE_LTR, advances,
                 advancesIndex);
         setTextSize(oldSize);
@@ -2096,6 +2296,7 @@
      */
     public int getTextRunCursor(char[] text, int contextStart, int contextLength,
             int dir, int offset, int cursorOpt) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         int contextEnd = contextStart + contextLength;
         if (((contextStart | contextEnd | offset | (contextEnd - contextStart)
                 | (offset - contextStart) | (contextEnd - offset)
@@ -2104,7 +2305,7 @@
             throw new IndexOutOfBoundsException();
         }
 
-        return native_getTextRunCursor(mNativePaint, text,
+        return nGetTextRunCursor(mNativePaint, text,
                 contextStart, contextLength, dir, offset, cursorOpt);
     }
 
@@ -2183,6 +2384,7 @@
      */
     public int getTextRunCursor(String text, int contextStart, int contextEnd,
             int dir, int offset, int cursorOpt) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         if (((contextStart | contextEnd | offset | (contextEnd - contextStart)
                 | (offset - contextStart) | (contextEnd - offset)
                 | (text.length() - contextEnd) | cursorOpt) < 0)
@@ -2190,7 +2392,7 @@
             throw new IndexOutOfBoundsException();
         }
 
-        return native_getTextRunCursor(mNativePaint, text,
+        return nGetTextRunCursor(mNativePaint, text,
                 contextStart, contextEnd, dir, offset, cursorOpt);
     }
 
@@ -2209,10 +2411,11 @@
      */
     public void getTextPath(char[] text, int index, int count,
                             float x, float y, Path path) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         if ((index | count) < 0 || index + count > text.length) {
             throw new ArrayIndexOutOfBoundsException();
         }
-        native_getTextPath(mNativePaint, mNativeTypeface, mBidiFlags, text, index, count, x, y,
+        nGetTextPath(mNativePaint, mNativeTypeface, mBidiFlags, text, index, count, x, y,
                 path.ni());
     }
 
@@ -2231,10 +2434,11 @@
      */
     public void getTextPath(String text, int start, int end,
                             float x, float y, Path path) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         if ((start | end | (end - start) | (text.length() - end)) < 0) {
             throw new IndexOutOfBoundsException();
         }
-        native_getTextPath(mNativePaint, mNativeTypeface, mBidiFlags, text, start, end, x, y,
+        nGetTextPath(mNativePaint, mNativeTypeface, mBidiFlags, text, start, end, x, y,
                 path.ni());
     }
 
@@ -2249,13 +2453,14 @@
      *               allocated by the caller.
      */
     public void getTextBounds(String text, int start, int end, Rect bounds) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         if ((start | end | (end - start) | (text.length() - end)) < 0) {
             throw new IndexOutOfBoundsException();
         }
         if (bounds == null) {
             throw new NullPointerException("need bounds Rect");
         }
-        nativeGetStringBounds(mNativePaint, mNativeTypeface, text, start, end, mBidiFlags, bounds);
+        nGetStringBounds(mNativePaint, mNativeTypeface, text, start, end, mBidiFlags, bounds);
     }
 
     /**
@@ -2269,13 +2474,14 @@
      *               allocated by the caller.
      */
     public void getTextBounds(char[] text, int index, int count, Rect bounds) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         if ((index | count) < 0 || index + count > text.length) {
             throw new ArrayIndexOutOfBoundsException();
         }
         if (bounds == null) {
             throw new NullPointerException("need bounds Rect");
         }
-        nativeGetCharArrayBounds(mNativePaint, mNativeTypeface, text, index, count, mBidiFlags,
+        nGetCharArrayBounds(mNativePaint, mNativeTypeface, text, index, count, mBidiFlags,
             bounds);
     }
 
@@ -2296,7 +2502,8 @@
      * @return true if the typeface has a glyph for the string
      */
     public boolean hasGlyph(String string) {
-        return native_hasGlyph(mNativePaint, mNativeTypeface, mBidiFlags, string);
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+        return nHasGlyph(mNativePaint, mNativeTypeface, mBidiFlags, string);
     }
 
     /**
@@ -2337,6 +2544,7 @@
      */
     public float getRunAdvance(char[] text, int start, int end, int contextStart, int contextEnd,
             boolean isRtl, int offset) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         if (text == null) {
             throw new IllegalArgumentException("text cannot be null");
         }
@@ -2349,7 +2557,7 @@
             return 0.0f;
         }
         // TODO: take mCompatScaling into account (or eliminate compat scaling)?
-        return native_getRunAdvance(mNativePaint, mNativeTypeface, text, start, end,
+        return nGetRunAdvance(mNativePaint, mNativeTypeface, text, start, end,
                 contextStart, contextEnd, isRtl, offset);
     }
 
@@ -2367,6 +2575,7 @@
      */
     public float getRunAdvance(CharSequence text, int start, int end, int contextStart,
             int contextEnd, boolean isRtl, int offset) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         if (text == null) {
             throw new IllegalArgumentException("text cannot be null");
         }
@@ -2417,6 +2626,7 @@
      */
     public int getOffsetForAdvance(char[] text, int start, int end, int contextStart,
             int contextEnd, boolean isRtl, float advance) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         if (text == null) {
             throw new IllegalArgumentException("text cannot be null");
         }
@@ -2426,7 +2636,7 @@
             throw new IndexOutOfBoundsException();
         }
         // TODO: take mCompatScaling into account (or eliminate compat scaling)?
-        return native_getOffsetForAdvance(mNativePaint, mNativeTypeface, text, start, end,
+        return nGetOffsetForAdvance(mNativePaint, mNativeTypeface, text, start, end,
                 contextStart, contextEnd, isRtl, advance);
     }
 
@@ -2444,6 +2654,7 @@
      */
     public int getOffsetForAdvance(CharSequence text, int start, int end, int contextStart,
             int contextEnd, boolean isRtl, float advance) {
+        if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
         if (text == null) {
             throw new IllegalArgumentException("text cannot be null");
         }
@@ -2464,90 +2675,88 @@
     @Override
     protected void finalize() throws Throwable {
         try {
-            finalizer(mNativePaint);
-            mNativePaint = 0;
+            if (mNativePaint != 0) {
+                nFinalizer(mNativePaint);
+                mNativePaint = 0;
+            }
         } finally {
             super.finalize();
         }
     }
 
-    private static native long native_init();
-    private static native long native_initWithPaint(long paint);
-    private static native void native_reset(long native_object);
-    private static native void native_set(long native_dst, long native_src);
-    private static native int native_getStyle(long native_object);
-    private static native void native_setStyle(long native_object, int style);
-    private static native int native_getStrokeCap(long native_object);
-    private static native void native_setStrokeCap(long native_object, int cap);
-    private static native int native_getStrokeJoin(long native_object);
-    private static native void native_setStrokeJoin(long native_object,
+    private static native long nInit();
+    private static native long nInitWithPaint(long paint);
+    private static native void nReset(long paintPtr);
+    private static native void nSet(long paintPtrDest, long paintPtrSrc);
+    private static native int nGetStyle(long paintPtr);
+    private static native void nSetStyle(long paintPtr, int style);
+    private static native int nGetStrokeCap(long paintPtr);
+    private static native void nSetStrokeCap(long paintPtr, int cap);
+    private static native int nGetStrokeJoin(long paintPtr);
+    private static native void nSetStrokeJoin(long paintPtr,
                                                     int join);
-    private static native boolean native_getFillPath(long native_object,
+    private static native boolean nGetFillPath(long paintPtr,
                                                      long src, long dst);
-    private static native long native_setShader(long native_object, long shader);
-    private static native long native_setColorFilter(long native_object,
+    private static native long nSetShader(long paintPtr, long shader);
+    private static native long nSetColorFilter(long paintPtr,
                                                     long filter);
-    private static native long native_setXfermode(long native_object,
+    private static native long nSetXfermode(long paintPtr,
                                                   long xfermode);
-    private static native long native_setPathEffect(long native_object,
+    private static native long nSetPathEffect(long paintPtr,
                                                     long effect);
-    private static native long native_setMaskFilter(long native_object,
+    private static native long nSetMaskFilter(long paintPtr,
                                                     long maskfilter);
-    private static native long native_setTypeface(long native_object,
+    private static native long nSetTypeface(long paintPtr,
                                                   long typeface);
-    private static native long native_setRasterizer(long native_object,
+    private static native long nSetRasterizer(long paintPtr,
                                                    long rasterizer);
 
-    private static native int native_getTextAlign(long native_object);
-    private static native void native_setTextAlign(long native_object,
+    private static native int nGetTextAlign(long paintPtr);
+    private static native void nSetTextAlign(long paintPtr,
                                                    int align);
 
-    private static native void native_setTextLocale(long native_object,
+    private static native void nSetTextLocale(long paintPtr,
                                                     String locale);
 
-    private static native int native_getTextGlyphs(long native_object,
-            String text, int start, int end, int contextStart, int contextEnd,
-            int flags, char[] glyphs);
-
-    private static native float native_getTextAdvances(long native_object, long native_typeface,
+    private static native float nGetTextAdvances(long paintPtr, long typefacePtr,
             char[] text, int index, int count, int contextIndex, int contextCount,
             int bidiFlags, float[] advances, int advancesIndex);
-    private static native float native_getTextAdvances(long native_object, long native_typeface,
+    private static native float nGetTextAdvances(long paintPtr, long typefacePtr,
             String text, int start, int end, int contextStart, int contextEnd,
             int bidiFlags, float[] advances, int advancesIndex);
 
-    private native int native_getTextRunCursor(long native_object, char[] text,
+    private native int nGetTextRunCursor(long paintPtr, char[] text,
             int contextStart, int contextLength, int dir, int offset, int cursorOpt);
-    private native int native_getTextRunCursor(long native_object, String text,
+    private native int nGetTextRunCursor(long paintPtr, String text,
             int contextStart, int contextEnd, int dir, int offset, int cursorOpt);
 
-    private static native void native_getTextPath(long native_object, long native_typeface,
+    private static native void nGetTextPath(long paintPtr, long typefacePtr,
             int bidiFlags, char[] text, int index, int count, float x, float y, long path);
-    private static native void native_getTextPath(long native_object, long native_typeface,
+    private static native void nGetTextPath(long paintPtr, long typefacePtr,
             int bidiFlags, String text, int start, int end, float x, float y, long path);
-    private static native void nativeGetStringBounds(long nativePaint, long native_typeface,
+    private static native void nGetStringBounds(long nativePaint, long typefacePtr,
                                 String text, int start, int end, int bidiFlags, Rect bounds);
-    private static native void nativeGetCharArrayBounds(long nativePaint, long native_typeface,
+    private static native void nGetCharArrayBounds(long nativePaint, long typefacePtr,
                                 char[] text, int index, int count, int bidiFlags, Rect bounds);
-    private static native void finalizer(long nativePaint);
+    private static native void nFinalizer(long nativePaint);
 
-    private static native void native_setShadowLayer(long native_object,
+    private static native void nSetShadowLayer(long paintPtr,
             float radius, float dx, float dy, int color);
-    private static native boolean native_hasShadowLayer(long native_object);
+    private static native boolean nHasShadowLayer(long paintPtr);
 
-    private static native float native_getLetterSpacing(long native_object);
-    private static native void native_setLetterSpacing(long native_object,
+    private static native float nGetLetterSpacing(long paintPtr);
+    private static native void nSetLetterSpacing(long paintPtr,
                                                        float letterSpacing);
-    private static native void native_setFontFeatureSettings(long native_object,
+    private static native void nSetFontFeatureSettings(long paintPtr,
                                                              String settings);
-    private static native int native_getHyphenEdit(long native_object);
-    private static native void native_setHyphenEdit(long native_object, int hyphen);
-    private static native boolean native_hasGlyph(long native_object, long native_typeface,
+    private static native int nGetHyphenEdit(long paintPtr);
+    private static native void nSetHyphenEdit(long paintPtr, int hyphen);
+    private static native boolean nHasGlyph(long paintPtr, long typefacePtr,
             int bidiFlags, String string);
-    private static native float native_getRunAdvance(long native_object, long native_typeface,
+    private static native float nGetRunAdvance(long paintPtr, long typefacePtr,
             char[] text, int start, int end, int contextStart, int contextEnd, boolean isRtl,
             int offset);
-    private static native int native_getOffsetForAdvance(long native_object,
-            long native_typeface, char[] text, int start, int end, int contextStart, int contextEnd,
+    private static native int nGetOffsetForAdvance(long paintPtr,
+            long typefacePtr, char[] text, int start, int end, int contextStart, int contextEnd,
             boolean isRtl, float advance);
 }
diff --git a/graphics/tests/graphicstests/src/android/graphics/PaintTest.java b/graphics/tests/graphicstests/src/android/graphics/PaintTest.java
index b67aa7d..6763dd1 100644
--- a/graphics/tests/graphicstests/src/android/graphics/PaintTest.java
+++ b/graphics/tests/graphicstests/src/android/graphics/PaintTest.java
@@ -162,4 +162,60 @@
         } catch (IndexOutOfBoundsException e) {
         }
     }
+
+    public void testMeasureTextBidi() {
+        Paint p = new Paint();
+        {
+            String bidiText = "abc \u0644\u063A\u0629 def";
+            p.setBidiFlags(Paint.BIDI_LTR);
+            float width = p.measureText(bidiText, 0, 4);
+            p.setBidiFlags(Paint.BIDI_RTL);
+            width += p.measureText(bidiText, 4, 7);
+            p.setBidiFlags(Paint.BIDI_LTR);
+            width += p.measureText(bidiText, 7, bidiText.length());
+            assertEquals(width, p.measureText(bidiText), 1.0f);
+        }
+        {
+            String bidiText = "abc \u0644\u063A\u0629 def";
+            p.setBidiFlags(Paint.BIDI_DEFAULT_LTR);
+            float width = p.measureText(bidiText, 0, 4);
+            width += p.measureText(bidiText, 4, 7);
+            width += p.measureText(bidiText, 7, bidiText.length());
+            assertEquals(width, p.measureText(bidiText), 1.0f);
+        }
+        {
+            String bidiText = "abc \u0644\u063A\u0629 def";
+            p.setBidiFlags(Paint.BIDI_FORCE_LTR);
+            float width = p.measureText(bidiText, 0, 4);
+            width += p.measureText(bidiText, 4, 7);
+            width += p.measureText(bidiText, 7, bidiText.length());
+            assertEquals(width, p.measureText(bidiText), 1.0f);
+        }
+        {
+            String bidiText = "\u0644\u063A\u0629 abc \u0644\u063A\u0629";
+            p.setBidiFlags(Paint.BIDI_RTL);
+            float width = p.measureText(bidiText, 0, 4);
+            p.setBidiFlags(Paint.BIDI_LTR);
+            width += p.measureText(bidiText, 4, 7);
+            p.setBidiFlags(Paint.BIDI_RTL);
+            width += p.measureText(bidiText, 7, bidiText.length());
+            assertEquals(width, p.measureText(bidiText), 1.0f);
+        }
+        {
+            String bidiText = "\u0644\u063A\u0629 abc \u0644\u063A\u0629";
+            p.setBidiFlags(Paint.BIDI_DEFAULT_RTL);
+            float width = p.measureText(bidiText, 0, 4);
+            width += p.measureText(bidiText, 4, 7);
+            width += p.measureText(bidiText, 7, bidiText.length());
+            assertEquals(width, p.measureText(bidiText), 1.0f);
+        }
+        {
+            String bidiText = "\u0644\u063A\u0629 abc \u0644\u063A\u0629";
+            p.setBidiFlags(Paint.BIDI_FORCE_RTL);
+            float width = p.measureText(bidiText, 0, 4);
+            width += p.measureText(bidiText, 4, 7);
+            width += p.measureText(bidiText, 7, bidiText.length());
+            assertEquals(width, p.measureText(bidiText), 1.0f);
+        }
+    }
 }
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index cc0943f..eaade34 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -209,6 +209,7 @@
 
 LOCAL_SRC_FILES += \
     tests/TestContext.cpp \
+    tests/TreeContentAnimation.cpp \
     tests/main.cpp
 
 include $(BUILD_EXECUTABLE)
diff --git a/libs/hwui/CanvasState.cpp b/libs/hwui/CanvasState.cpp
index c128ca7..eca71c6 100644
--- a/libs/hwui/CanvasState.cpp
+++ b/libs/hwui/CanvasState.cpp
@@ -28,10 +28,21 @@
         , mWidth(-1)
         , mHeight(-1)
         , mSaveCount(1)
-        , mFirstSnapshot(new Snapshot)
         , mCanvas(renderer)
-        , mSnapshot(mFirstSnapshot) {
+        , mSnapshot(&mFirstSnapshot) {
+}
 
+CanvasState::~CanvasState() {
+    // First call freeSnapshot on all but mFirstSnapshot
+    // to invoke all the dtors
+    freeAllSnapshots();
+
+    // Now actually release the memory
+    while (mSnapshotPool) {
+        void* temp = mSnapshotPool;
+        mSnapshotPool = mSnapshotPool->previous;
+        free(temp);
+    }
 }
 
 void CanvasState::initializeSaveStack(
@@ -41,11 +52,12 @@
     if (mWidth != viewportWidth || mHeight != viewportHeight) {
         mWidth = viewportWidth;
         mHeight = viewportHeight;
-        mFirstSnapshot->initializeViewport(viewportWidth, viewportHeight);
+        mFirstSnapshot.initializeViewport(viewportWidth, viewportHeight);
         mCanvas.onViewportInitialized();
     }
 
-    mSnapshot = new Snapshot(mFirstSnapshot,
+    freeAllSnapshots();
+    mSnapshot = allocSnapshot(&mFirstSnapshot,
             SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
     mSnapshot->setClip(clipLeft, clipTop, clipRight, clipBottom);
     mSnapshot->fbo = mCanvas.getTargetFbo();
@@ -53,6 +65,38 @@
     mSaveCount = 1;
 }
 
+Snapshot* CanvasState::allocSnapshot(Snapshot* previous, int savecount) {
+    void* memory;
+    if (mSnapshotPool) {
+        memory = mSnapshotPool;
+        mSnapshotPool = mSnapshotPool->previous;
+        mSnapshotPoolCount--;
+    } else {
+        memory = malloc(sizeof(Snapshot));
+    }
+    return new (memory) Snapshot(previous, savecount);
+}
+
+void CanvasState::freeSnapshot(Snapshot* snapshot) {
+    snapshot->~Snapshot();
+    // Arbitrary number, just don't let this grown unbounded
+    if (mSnapshotPoolCount > 10) {
+        free((void*) snapshot);
+    } else {
+        snapshot->previous = mSnapshotPool;
+        mSnapshotPool = snapshot;
+        mSnapshotPoolCount++;
+    }
+}
+
+void CanvasState::freeAllSnapshots() {
+    while (mSnapshot != &mFirstSnapshot) {
+        Snapshot* temp = mSnapshot;
+        mSnapshot = mSnapshot->previous;
+        freeSnapshot(temp);
+    }
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 // Save (layer)
 ///////////////////////////////////////////////////////////////////////////////
@@ -64,7 +108,7 @@
  * stack, and ensures restoreToCount() doesn't call back into subclass overrides.
  */
 int CanvasState::saveSnapshot(int flags) {
-    mSnapshot = new Snapshot(mSnapshot, flags);
+    mSnapshot = allocSnapshot(mSnapshot, flags);
     return mSaveCount++;
 }
 
@@ -76,14 +120,16 @@
  * Guaranteed to restore without side-effects.
  */
 void CanvasState::restoreSnapshot() {
-    sp<Snapshot> toRemove = mSnapshot;
-    sp<Snapshot> toRestore = mSnapshot->previous;
+    Snapshot* toRemove = mSnapshot;
+    Snapshot* toRestore = mSnapshot->previous;
 
     mSaveCount--;
     mSnapshot = toRestore;
 
     // subclass handles restore implementation
     mCanvas.onSnapshotRestored(*toRemove, *toRestore);
+
+    freeSnapshot(toRemove);
 }
 
 void CanvasState::restore() {
diff --git a/libs/hwui/CanvasState.h b/libs/hwui/CanvasState.h
index f0fb9ba..be57f44 100644
--- a/libs/hwui/CanvasState.h
+++ b/libs/hwui/CanvasState.h
@@ -17,12 +17,12 @@
 #ifndef ANDROID_HWUI_CANVAS_STATE_H
 #define ANDROID_HWUI_CANVAS_STATE_H
 
+#include "Snapshot.h"
+
 #include <SkMatrix.h>
 #include <SkPath.h>
 #include <SkRegion.h>
 
-#include "Snapshot.h"
-
 namespace android {
 namespace uirenderer {
 
@@ -74,6 +74,7 @@
 class CanvasState {
 public:
     CanvasState(CanvasStateClient& renderer);
+    ~CanvasState();
 
     /**
      * Initializes the first snapshot, computing the projection matrix,
@@ -157,11 +158,15 @@
     int getHeight() const { return mHeight; }
     bool clipIsSimple() const { return currentSnapshot()->clipIsSimple(); }
 
-    inline const Snapshot* currentSnapshot() const { return mSnapshot.get(); }
-    inline Snapshot* writableSnapshot() { return mSnapshot.get(); }
-    inline const Snapshot* firstSnapshot() const { return mFirstSnapshot.get(); }
+    inline const Snapshot* currentSnapshot() const { return mSnapshot; }
+    inline Snapshot* writableSnapshot() { return mSnapshot; }
+    inline const Snapshot* firstSnapshot() const { return &mFirstSnapshot; }
 
 private:
+    Snapshot* allocSnapshot(Snapshot* previous, int savecount);
+    void freeSnapshot(Snapshot* snapshot);
+    void freeAllSnapshots();
+
     /// indicates that the clip has been changed since the last time it was consumed
     bool mDirtyClip;
 
@@ -172,13 +177,18 @@
     int mSaveCount;
 
     /// Base state
-    sp<Snapshot> mFirstSnapshot;
+    Snapshot mFirstSnapshot;
 
     /// Host providing callbacks
     CanvasStateClient& mCanvas;
 
     /// Current state
-    sp<Snapshot> mSnapshot;
+    Snapshot* mSnapshot;
+
+    // Pool of allocated snapshots to re-use
+    // NOTE: The dtors have already been invoked!
+    Snapshot* mSnapshotPool = nullptr;
+    int mSnapshotPoolCount = 0;
 
 }; // class CanvasState
 
diff --git a/libs/hwui/ClipArea.cpp b/libs/hwui/ClipArea.cpp
index 8e7efb4..a9d1e42 100644
--- a/libs/hwui/ClipArea.cpp
+++ b/libs/hwui/ClipArea.cpp
@@ -23,14 +23,6 @@
 namespace android {
 namespace uirenderer {
 
-static bool intersect(Rect& r, const Rect& r2) {
-    bool hasIntersection = r.intersect(r2);
-    if (!hasIntersection) {
-        r.setEmpty();
-    }
-    return hasIntersection;
-}
-
 static void handlePoint(Rect& transformedBounds, const Matrix4& transform, float x, float y) {
     Vertex v = {x, y};
     transform.mapPoint(v.x, v.y);
@@ -67,9 +59,8 @@
     return mTransform == other.mTransform;
 }
 
-bool TransformedRectangle::intersectWith(const TransformedRectangle& other) {
-    Rect translatedBounds(other.mBounds);
-    return intersect(mBounds, translatedBounds);
+void TransformedRectangle::intersectWith(const TransformedRectangle& other) {
+    mBounds.doIntersect(other.mBounds);
 }
 
 bool TransformedRectangle::isEmpty() const {
@@ -146,7 +137,7 @@
         if (index == 0) {
             bounds = tr.transformedBounds();
         } else {
-            bounds.intersect(tr.transformedBounds());
+            bounds.doIntersect(tr.transformedBounds());
         }
     }
     return bounds;
@@ -275,10 +266,7 @@
     if (transform->rectToRect()) {
         Rect transformed(r);
         transform->mapRect(transformed);
-        bool hasIntersection = mClipRect.intersect(transformed);
-        if (!hasIntersection) {
-            mClipRect.setEmpty();
-        }
+        mClipRect.doIntersect(transformed);
         return;
     }
 
diff --git a/libs/hwui/ClipArea.h b/libs/hwui/ClipArea.h
index 38fefe5..f88fd92 100644
--- a/libs/hwui/ClipArea.h
+++ b/libs/hwui/ClipArea.h
@@ -33,7 +33,7 @@
     TransformedRectangle(const Rect& bounds, const Matrix4& transform);
 
     bool canSimplyIntersectWith(const TransformedRectangle& other) const;
-    bool intersectWith(const TransformedRectangle& other);
+    void intersectWith(const TransformedRectangle& other);
 
     bool isEmpty() const;
 
diff --git a/libs/hwui/DeferredDisplayList.cpp b/libs/hwui/DeferredDisplayList.cpp
index a81ffb9..0c29a9e 100644
--- a/libs/hwui/DeferredDisplayList.cpp
+++ b/libs/hwui/DeferredDisplayList.cpp
@@ -44,6 +44,12 @@
 #define DEBUG_COLOR_MERGEDBATCH      0x5f7f7fff
 #define DEBUG_COLOR_MERGEDBATCH_SOLO 0x5f7fff7f
 
+static bool avoidOverdraw() {
+    // Don't avoid overdraw when visualizing it, since that makes it harder to
+    // debug where it's coming from, and when the problem occurs.
+    return !Properties::debugOverdraw;
+};
+
 /////////////////////////////////////////////////////////////////////////////////
 // Operation Batches
 /////////////////////////////////////////////////////////////////////////////////
@@ -218,7 +224,10 @@
         // if paints are equal, then modifiers + paint attribs don't need to be compared
         if (op->mPaint == mOps[0].op->mPaint) return true;
 
-        if (op->getPaintAlpha() != mOps[0].op->getPaintAlpha()) return false;
+        if (PaintUtils::getAlphaDirect(op->mPaint)
+                != PaintUtils::getAlphaDirect(mOps[0].op->mPaint)) {
+            return false;
+        }
 
         if (op->mPaint && mOps[0].op->mPaint &&
             op->mPaint->getColorFilter() != mOps[0].op->mPaint->getColorFilter()) {
@@ -495,7 +504,7 @@
             && mSaveStack.empty()
             && !state->mRoundRectClipState;
 
-    if (CC_LIKELY(mAvoidOverdraw) && mBatches.size() &&
+    if (CC_LIKELY(avoidOverdraw()) && mBatches.size() &&
             state->mClipSideFlags != kClipSide_ConservativeFull &&
             deferInfo.opaqueOverBounds && state->mBounds.contains(mBounds)) {
         // avoid overdraw by resetting drawing state + discarding drawing ops
@@ -533,7 +542,11 @@
 
         if (deferInfo.mergeable) {
             // Try to merge with any existing batch with same mergeId.
-            if (mMergingBatches[deferInfo.batchId].get(deferInfo.mergeId, targetBatch)) {
+            std::unordered_map<mergeid_t, DrawBatch*>& mergingBatch
+                    = mMergingBatches[deferInfo.batchId];
+            auto getResult = mergingBatch.find(deferInfo.mergeId);
+            if (getResult != mergingBatch.end()) {
+                targetBatch = getResult->second;
                 if (!((MergingDrawBatch*) targetBatch)->canMergeWith(op, state)) {
                     targetBatch = nullptr;
                 }
@@ -577,7 +590,8 @@
         if (deferInfo.mergeable) {
             targetBatch = new MergingDrawBatch(deferInfo,
                     renderer.getViewportWidth(), renderer.getViewportHeight());
-            mMergingBatches[deferInfo.batchId].put(deferInfo.mergeId, targetBatch);
+            mMergingBatches[deferInfo.batchId].insert(
+                    std::make_pair(deferInfo.mergeId, targetBatch));
         } else {
             targetBatch = new DrawBatch(deferInfo);
             mBatchLookup[deferInfo.batchId] = targetBatch;
@@ -642,7 +656,7 @@
     // save and restore so that reordering doesn't affect final state
     renderer.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
 
-    if (CC_LIKELY(mAvoidOverdraw)) {
+    if (CC_LIKELY(avoidOverdraw())) {
         for (unsigned int i = 1; i < mBatches.size(); i++) {
             if (mBatches[i] && mBatches[i]->coversBounds(mBounds)) {
                 discardDrawingBatches(i - 1);
diff --git a/libs/hwui/DeferredDisplayList.h b/libs/hwui/DeferredDisplayList.h
index 4f2dca5..7873fbd 100644
--- a/libs/hwui/DeferredDisplayList.h
+++ b/libs/hwui/DeferredDisplayList.h
@@ -17,9 +17,10 @@
 #ifndef ANDROID_HWUI_DEFERRED_DISPLAY_LIST_H
 #define ANDROID_HWUI_DEFERRED_DISPLAY_LIST_H
 
+#include <unordered_map>
+
 #include <utils/Errors.h>
 #include <utils/LinearAllocator.h>
-#include <utils/TinyHashMap.h>
 
 #include "Matrix.h"
 #include "OpenGLRenderer.h"
@@ -82,8 +83,8 @@
 class DeferredDisplayList {
     friend struct DeferStateStruct; // used to give access to allocator
 public:
-    DeferredDisplayList(const Rect& bounds, bool avoidOverdraw = true) :
-            mBounds(bounds), mAvoidOverdraw(avoidOverdraw) {
+    DeferredDisplayList(const Rect& bounds)
+            : mBounds(bounds) {
         clear();
     }
     ~DeferredDisplayList() { clear(); }
@@ -151,7 +152,6 @@
 
     // layer space bounds of rendering
     Rect mBounds;
-    const bool mAvoidOverdraw;
 
     /**
      * At defer time, stores the *defer time* savecount of save/saveLayer ops that were deferred, so
@@ -177,7 +177,7 @@
      * MergingDrawBatch of that id. These ids are unique per draw type and guaranteed to not
      * collide, which avoids the need to resolve mergeid collisions.
      */
-    TinyHashMap<mergeid_t, DrawBatch*> mMergingBatches[kOpBatch_Count];
+    std::unordered_map<mergeid_t, DrawBatch*> mMergingBatches[kOpBatch_Count];
 
     LinearAllocator mAllocator;
 };
diff --git a/libs/hwui/DeferredLayerUpdater.cpp b/libs/hwui/DeferredLayerUpdater.cpp
index 38f2363..7038334 100644
--- a/libs/hwui/DeferredLayerUpdater.cpp
+++ b/libs/hwui/DeferredLayerUpdater.cpp
@@ -47,7 +47,8 @@
 }
 
 void DeferredLayerUpdater::setPaint(const SkPaint* paint) {
-    OpenGLRenderer::getAlphaAndModeDirect(paint, &mAlpha, &mMode);
+    mAlpha = PaintUtils::getAlphaDirect(paint);
+    mMode = PaintUtils::getXfermodeDirect(paint);
     SkColorFilter* colorFilter = (paint) ? paint->getColorFilter() : nullptr;
     SkRefCnt_SafeAssign(mColorFilter, colorFilter);
 }
diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h
index dc5cb8b..ddfc533 100644
--- a/libs/hwui/DisplayListOp.h
+++ b/libs/hwui/DisplayListOp.h
@@ -172,10 +172,6 @@
     void setQuickRejected(bool quickRejected) { mQuickRejected = quickRejected; }
     bool getQuickRejected() { return mQuickRejected; }
 
-    inline int getPaintAlpha() const {
-        return OpenGLRenderer::getAlphaDirect(mPaint);
-    }
-
     virtual bool hasTextShadow() const {
         return false;
     }
@@ -213,7 +209,7 @@
 
         if (state.mAlpha != 1.0f) return false;
 
-        SkXfermode::Mode mode = OpenGLRenderer::getXfermodeDirect(mPaint);
+        SkXfermode::Mode mode = PaintUtils::getXfermodeDirect(mPaint);
         return (mode == SkXfermode::kSrcOver_Mode ||
                 mode == SkXfermode::kSrc_Mode);
 
@@ -249,8 +245,8 @@
 
     virtual bool getLocalBounds(Rect& localBounds) override {
         localBounds.set(mLocalBounds);
-        OpenGLRenderer::TextShadow textShadow;
-        if (OpenGLRenderer::getTextShadow(mPaint, &textShadow)) {
+        PaintUtils::TextShadow textShadow;
+        if (PaintUtils::getTextShadow(mPaint, &textShadow)) {
             Rect shadow(mLocalBounds);
             shadow.translate(textShadow.dx, textShadow.dx);
             shadow.outset(textShadow.radius);
@@ -372,8 +368,8 @@
 
 private:
     bool isSaveLayerAlpha() const {
-        SkXfermode::Mode mode = OpenGLRenderer::getXfermodeDirect(mPaint);
-        int alpha = OpenGLRenderer::getAlphaDirect(mPaint);
+        SkXfermode::Mode mode = PaintUtils::getXfermodeDirect(mPaint);
+        int alpha = PaintUtils::getAlphaDirect(mPaint);
         return alpha < 255 && mode == SkXfermode::kSrcOver_Mode;
     }
 
@@ -691,7 +687,7 @@
         // TODO: support clipped bitmaps by handling them in SET_TEXTURE
         deferInfo.mergeable = state.mMatrix.isSimple() && state.mMatrix.positiveScale() &&
                 !state.mClipSideFlags &&
-                OpenGLRenderer::getXfermodeDirect(mPaint) == SkXfermode::kSrcOver_Mode &&
+                PaintUtils::getXfermodeDirect(mPaint) == SkXfermode::kSrcOver_Mode &&
                 (mBitmap->colorType() != kAlpha_8_SkColorType);
     }
 
@@ -895,7 +891,7 @@
         deferInfo.batchId = DeferredDisplayList::kOpBatch_Patch;
         deferInfo.mergeId = getAtlasEntry(renderer) ? (mergeid_t) mEntry->getMergeId() : (mergeid_t) mBitmap;
         deferInfo.mergeable = state.mMatrix.isPureTranslate() &&
-                OpenGLRenderer::getXfermodeDirect(mPaint) == SkXfermode::kSrcOver_Mode;
+                PaintUtils::getXfermodeDirect(mPaint) == SkXfermode::kSrcOver_Mode;
         deferInfo.opaqueOverBounds = isOpaqueOverBounds(state) && mBitmap->isOpaque();
     }
 
@@ -1241,7 +1237,7 @@
     }
 
     virtual bool hasTextShadow() const override {
-        return OpenGLRenderer::hasTextShadow(mPaint);
+        return PaintUtils::hasTextShadow(mPaint);
     }
 
     virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo,
@@ -1330,7 +1326,7 @@
 
         deferInfo.mergeable = state.mMatrix.isPureTranslate()
                 && !hasDecorations
-                && OpenGLRenderer::getXfermodeDirect(mPaint) == SkXfermode::kSrcOver_Mode;
+                && PaintUtils::getXfermodeDirect(mPaint) == SkXfermode::kSrcOver_Mode;
     }
 
     virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override {
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index 4b9d4f9..ccf0b48 100644
--- a/libs/hwui/FontRenderer.cpp
+++ b/libs/hwui/FontRenderer.cpp
@@ -667,14 +667,6 @@
     return mDrawn;
 }
 
-void FontRenderer::removeFont(const Font* font) {
-    mActiveFonts.remove(font->getDescription());
-
-    if (mCurrentFont == font) {
-        mCurrentFont = nullptr;
-    }
-}
-
 void FontRenderer::blurImage(uint8_t** image, int32_t width, int32_t height, float radius) {
     uint32_t intRadius = Blur::convertRadiusToInt(radius);
 #ifdef ANDROID_ENABLE_RENDERSCRIPT
diff --git a/libs/hwui/FontRenderer.h b/libs/hwui/FontRenderer.h
index 936c838..8172312 100644
--- a/libs/hwui/FontRenderer.h
+++ b/libs/hwui/FontRenderer.h
@@ -147,8 +147,6 @@
             float x3, float y3, float u3, float v3,
             float x4, float y4, float u4, float v4, CacheTexture* texture);
 
-    void removeFont(const Font* font);
-
     void checkTextureUpdate();
 
     void setTextureDirty() {
diff --git a/libs/hwui/Glop.h b/libs/hwui/Glop.h
index fa20b08..4785ea4 100644
--- a/libs/hwui/Glop.h
+++ b/libs/hwui/Glop.h
@@ -135,10 +135,6 @@
     } fill;
 
     struct Transform {
-        // Orthographic projection matrix for current FBO
-        // TODO: move out of Glop, since this is static per FBO
-        Matrix4 ortho;
-
         // modelView transform, accounting for delta between mesh transform and content of the mesh
         // often represents x/y offsets within command, or scaling for mesh unit size
         Matrix4 modelView;
diff --git a/libs/hwui/GlopBuilder.cpp b/libs/hwui/GlopBuilder.cpp
index 69559a7..fa166ae 100644
--- a/libs/hwui/GlopBuilder.cpp
+++ b/libs/hwui/GlopBuilder.cpp
@@ -461,11 +461,10 @@
 // Transform
 ////////////////////////////////////////////////////////////////////////////////
 
-void GlopBuilder::setTransform(const Matrix4& ortho, const Matrix4& canvas,
+void GlopBuilder::setTransform(const Matrix4& canvas,
         const int transformFlags) {
     TRIGGER_STAGE(kTransformStage);
 
-    mOutGlop->transform.ortho = ortho;
     mOutGlop->transform.canvas = canvas;
     mOutGlop->transform.transformFlags = transformFlags;
 }
diff --git a/libs/hwui/GlopBuilder.h b/libs/hwui/GlopBuilder.h
index 549bb21..8d05570 100644
--- a/libs/hwui/GlopBuilder.h
+++ b/libs/hwui/GlopBuilder.h
@@ -71,7 +71,7 @@
     GlopBuilder& setFillTextureLayer(Layer& layer, float alpha);
 
     GlopBuilder& setTransform(const Snapshot& snapshot, const int transformFlags) {
-        setTransform(snapshot.getOrthoMatrix(), *snapshot.transform, transformFlags);
+        setTransform(*snapshot.transform, transformFlags);
         return *this;
     }
 
@@ -102,8 +102,7 @@
     void setFill(int color, float alphaScale,
             SkXfermode::Mode mode, Blend::ModeOrderSwap modeUsage,
             const SkShader* shader, const SkColorFilter* colorFilter);
-    void setTransform(const Matrix4& ortho, const Matrix4& canvas,
-            const int transformFlags);
+    void setTransform(const Matrix4& canvas, const int transformFlags);
 
     enum StageFlags {
         kInitialStage = 0,
diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp
index 8d85289..f99d92b 100644
--- a/libs/hwui/Layer.cpp
+++ b/libs/hwui/Layer.cpp
@@ -170,7 +170,8 @@
 }
 
 void Layer::setPaint(const SkPaint* paint) {
-    OpenGLRenderer::getAlphaAndModeDirect(paint, &alpha, &mode);
+    alpha = PaintUtils::getAlphaDirect(paint);
+    mode = PaintUtils::getXfermodeDirect(paint);
     setColorFilter((paint) ? paint->getColorFilter() : nullptr);
 }
 
diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp
index c63b559..227271d 100644
--- a/libs/hwui/LayerRenderer.cpp
+++ b/libs/hwui/LayerRenderer.cpp
@@ -58,7 +58,7 @@
         mLayer->region.clear();
         dirty.set(0.0f, 0.0f, width, height);
     } else {
-        dirty.intersect(0.0f, 0.0f, width, height);
+        dirty.doIntersect(0.0f, 0.0f, width, height);
         android::Rect r(dirty.left, dirty.top, dirty.right, dirty.bottom);
         mLayer->region.subtractSelf(r);
     }
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index a401ce1..cd03ac4 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -488,7 +488,8 @@
     currentTransform()->mapRect(bounds);
 
     // Layers only make sense if they are in the framebuffer's bounds
-    if (bounds.intersect(mState.currentClipRect())) {
+    bounds.doIntersect(mState.currentClipRect());
+    if (!bounds.isEmpty()) {
         // We cannot work with sub-pixels in this case
         bounds.snapToPixelBoundaries();
 
@@ -497,23 +498,20 @@
         // of the framebuffer
         const Snapshot& previous = *(currentSnapshot()->previous);
         Rect previousViewport(0, 0, previous.getViewportWidth(), previous.getViewportHeight());
-        if (!bounds.intersect(previousViewport)) {
-            bounds.setEmpty();
-        } else if (fboLayer) {
+
+        bounds.doIntersect(previousViewport);
+        if (!bounds.isEmpty() && fboLayer) {
             clip.set(bounds);
             mat4 inverse;
             inverse.loadInverse(*currentTransform());
             inverse.mapRect(clip);
             clip.snapToPixelBoundaries();
-            if (clip.intersect(untransformedBounds)) {
+            clip.doIntersect(untransformedBounds);
+            if (!clip.isEmpty()) {
                 clip.translate(-untransformedBounds.left, -untransformedBounds.top);
                 bounds.set(untransformedBounds);
-            } else {
-                clip.setEmpty();
             }
         }
-    } else {
-        bounds.setEmpty();
     }
 }
 
@@ -540,7 +538,7 @@
         Rect bounds(left, top, right, bottom);
         Rect clip;
         calculateLayerBoundsAndClip(bounds, clip, true);
-        updateSnapshotIgnoreForLayer(bounds, clip, true, getAlphaDirect(paint));
+        updateSnapshotIgnoreForLayer(bounds, clip, true, PaintUtils::getAlphaDirect(paint));
 
         if (!mState.currentlyIgnored()) {
             writableSnapshot()->resetTransform(-bounds.left, -bounds.top, 0.0f);
@@ -615,7 +613,7 @@
     Rect clip;
     Rect bounds(left, top, right, bottom);
     calculateLayerBoundsAndClip(bounds, clip, fboLayer);
-    updateSnapshotIgnoreForLayer(bounds, clip, fboLayer, getAlphaDirect(paint));
+    updateSnapshotIgnoreForLayer(bounds, clip, fboLayer, PaintUtils::getAlphaDirect(paint));
 
     // Bail out if we won't draw in this snapshot
     if (mState.currentlyIgnored()) {
@@ -1038,7 +1036,8 @@
 }
 
 void OpenGLRenderer::dirtyLayerUnchecked(Rect& bounds, Region* region) {
-    if (CC_LIKELY(!bounds.isEmpty() && bounds.intersect(mState.currentClipRect()))) {
+    bounds.doIntersect(mState.currentClipRect());
+    if (!bounds.isEmpty()) {
         bounds.snapToPixelBoundaries();
         android::Rect dirty(bounds.left, bounds.top, bounds.right, bounds.bottom);
         if (!dirty.isEmpty()) {
@@ -1112,7 +1111,8 @@
             // is used, it should more closely duplicate the quickReject logic (in how it uses
             // snapToPixelBoundaries)
 
-            if (!clippedBounds.intersect(currentClip)) {
+            clippedBounds.doIntersect(currentClip);
+            if (clippedBounds.isEmpty()) {
                 // quick rejected
                 return true;
             }
@@ -1242,9 +1242,8 @@
         Rect bounds = tr.getBounds();
         if (transform.rectToRect()) {
             transform.mapRect(bounds);
-            if (!bounds.intersect(scissorBox)) {
-                bounds.setEmpty();
-            } else {
+            bounds.doIntersect(scissorBox);
+            if (!bounds.isEmpty()) {
                 handlePointNoTransform(rectangleVertices, bounds.left, bounds.top);
                 handlePointNoTransform(rectangleVertices, bounds.right, bounds.top);
                 handlePointNoTransform(rectangleVertices, bounds.left, bounds.bottom);
@@ -1405,7 +1404,7 @@
 
         setStencilFromClip();
     }
-    mRenderState.render(glop);
+    mRenderState.render(glop, currentSnapshot()->getOrthoMatrix());
     if (type == GlopRenderType::Standard && !mRenderState.stencil().isWriteEnabled()) {
         // TODO: specify more clearly when a draw should dirty the layer.
         // is writing to the stencil the only time we should ignore this?
@@ -1431,10 +1430,7 @@
             return;
         }
 
-        // Don't avoid overdraw when visualizing, since that makes it harder to
-        // debug where it's coming from, and when the problem occurs.
-        bool avoidOverdraw = !Properties::debugOverdraw;
-        DeferredDisplayList deferredList(mState.currentClipRect(), avoidOverdraw);
+        DeferredDisplayList deferredList(mState.currentClipRect());
         DeferStateStruct deferStruct(deferredList, *this, replayFlags);
         renderNode->defer(deferStruct, 0);
 
@@ -1958,8 +1954,8 @@
         FontRenderer& fontRenderer, int alpha, float x, float y) {
     mCaches.textureState().activateTexture(0);
 
-    TextShadow textShadow;
-    if (!getTextShadow(paint, &textShadow)) {
+    PaintUtils::TextShadow textShadow;
+    if (!PaintUtils::getTextShadow(paint, &textShadow)) {
         LOG_ALWAYS_FATAL("failed to query shadow attributes");
     }
 
@@ -1987,8 +1983,10 @@
     renderGlop(glop);
 }
 
+// TODO: remove this, once mState.currentlyIgnored captures snapshot alpha
 bool OpenGLRenderer::canSkipText(const SkPaint* paint) const {
-    float alpha = (hasTextShadow(paint) ? 1.0f : paint->getAlpha()) * currentSnapshot()->alpha;
+    float alpha = (PaintUtils::hasTextShadow(paint)
+            ? 1.0f : paint->getAlpha()) * currentSnapshot()->alpha;
     return MathUtils::isZero(alpha)
             && PaintUtils::getXfermode(paint->getXfermode()) == SkXfermode::kSrcOver_Mode;
 }
@@ -2017,11 +2015,10 @@
     FontRenderer& fontRenderer = mCaches.fontRenderer.getFontRenderer();
     fontRenderer.setFont(paint, SkMatrix::I());
 
-    int alpha;
-    SkXfermode::Mode mode;
-    getAlphaAndMode(paint, &alpha, &mode);
+    int alpha = PaintUtils::getAlphaDirect(paint) * currentSnapshot()->alpha;
+    SkXfermode::Mode mode = PaintUtils::getXfermodeDirect(paint);
 
-    if (CC_UNLIKELY(hasTextShadow(paint))) {
+    if (CC_UNLIKELY(PaintUtils::hasTextShadow(paint))) {
         drawTextShadow(paint, text, bytesCount, count, positions, fontRenderer,
                 alpha, 0.0f, 0.0f);
     }
@@ -2162,13 +2159,12 @@
         y = floorf(y + transform.getTranslateY() + 0.5f);
     }
 
-    int alpha;
-    SkXfermode::Mode mode;
-    getAlphaAndMode(paint, &alpha, &mode);
+    int alpha = PaintUtils::getAlphaDirect(paint) * currentSnapshot()->alpha;
+    SkXfermode::Mode mode = PaintUtils::getXfermodeDirect(paint);
 
     FontRenderer& fontRenderer = mCaches.fontRenderer.getFontRenderer();
 
-    if (CC_UNLIKELY(hasTextShadow(paint))) {
+    if (CC_UNLIKELY(PaintUtils::hasTextShadow(paint))) {
         fontRenderer.setFont(paint, SkMatrix::I());
         drawTextShadow(paint, text, bytesCount, count, positions, fontRenderer,
                 alpha, oldX, oldY);
@@ -2238,9 +2234,8 @@
     fontRenderer.setFont(paint, SkMatrix::I());
     fontRenderer.setTextureFiltering(true);
 
-    int alpha;
-    SkXfermode::Mode mode;
-    getAlphaAndMode(paint, &alpha, &mode);
+    int alpha = PaintUtils::getAlphaDirect(paint) * currentSnapshot()->alpha;
+    SkXfermode::Mode mode = PaintUtils::getXfermodeDirect(paint);
     TextDrawFunctor functor(this, 0.0f, 0.0f, false, alpha, mode, paint);
 
     const Rect* clip = &writableSnapshot()->getLocalClip();
@@ -2530,12 +2525,6 @@
     renderGlop(glop);
 }
 
-void OpenGLRenderer::getAlphaAndMode(const SkPaint* paint, int* alpha,
-        SkXfermode::Mode* mode) const {
-    getAlphaAndModeDirect(paint, alpha,  mode);
-    *alpha *= currentSnapshot()->alpha;
-}
-
 float OpenGLRenderer::getLayerAlpha(const Layer* layer) const {
     return (layer->getAlpha() / 255.0f) * currentSnapshot()->alpha;
 }
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 910af57..400c225 100755
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -260,57 +260,6 @@
     void endMark() const;
 
     /**
-     * Gets the alpha and xfermode out of a paint object. If the paint is null
-     * alpha will be 255 and the xfermode will be SRC_OVER. This method does
-     * not multiply the paint's alpha by the current snapshot's alpha, and does
-     * not replace the alpha with the overrideLayerAlpha
-     *
-     * @param paint The paint to extract values from
-     * @param alpha Where to store the resulting alpha
-     * @param mode Where to store the resulting xfermode
-     */
-    static inline void getAlphaAndModeDirect(const SkPaint* paint, int* alpha,
-            SkXfermode::Mode* mode) {
-        *mode = getXfermodeDirect(paint);
-        *alpha = getAlphaDirect(paint);
-    }
-
-    static inline SkXfermode::Mode getXfermodeDirect(const SkPaint* paint) {
-        if (!paint) return SkXfermode::kSrcOver_Mode;
-        return PaintUtils::getXfermode(paint->getXfermode());
-    }
-
-    static inline int getAlphaDirect(const SkPaint* paint) {
-        if (!paint) return 255;
-        return paint->getAlpha();
-    }
-
-    struct TextShadow {
-        SkScalar radius;
-        float dx;
-        float dy;
-        SkColor color;
-    };
-
-    static inline bool getTextShadow(const SkPaint* paint, TextShadow* textShadow) {
-        SkDrawLooper::BlurShadowRec blur;
-        if (paint && paint->getLooper() && paint->getLooper()->asABlurShadow(&blur)) {
-            if (textShadow) {
-                textShadow->radius = Blur::convertSigmaToRadius(blur.fSigma);
-                textShadow->dx = blur.fOffset.fX;
-                textShadow->dy = blur.fOffset.fY;
-                textShadow->color = blur.fColor;
-            }
-            return true;
-        }
-        return false;
-    }
-
-    static inline bool hasTextShadow(const SkPaint* paint) {
-        return getTextShadow(paint, nullptr);
-    }
-
-    /**
      * Build the best transform to use to rasterize text given a full
      * transform matrix, and whether filteration is needed.
      *
@@ -493,16 +442,6 @@
     void drawTextureLayer(Layer* layer, const Rect& rect);
 
     /**
-     * Gets the alpha and xfermode out of a paint object. If the paint is null
-     * alpha will be 255 and the xfermode will be SRC_OVER. Accounts for snapshot alpha.
-     *
-     * @param paint The paint to extract values from
-     * @param alpha Where to store the resulting alpha
-     * @param mode Where to store the resulting xfermode
-     */
-    inline void getAlphaAndMode(const SkPaint* paint, int* alpha, SkXfermode::Mode* mode) const;
-
-    /**
      * Gets the alpha from a layer, accounting for snapshot alpha
      *
      * @param layer The layer from which the alpha is extracted
diff --git a/libs/hwui/Rect.h b/libs/hwui/Rect.h
index 4c4cd3d..50199db 100644
--- a/libs/hwui/Rect.h
+++ b/libs/hwui/Rect.h
@@ -125,25 +125,32 @@
     }
 
     bool intersects(float l, float t, float r, float b) const {
-        return !intersectWith(l, t, r, b).isEmpty();
+        float tempLeft = std::max(left, l);
+        float tempTop = std::max(top, t);
+        float tempRight = std::min(right, r);
+        float tempBottom = std::min(bottom, b);
+
+        return ((tempLeft < tempRight) && (tempTop < tempBottom)); // !isEmpty
     }
 
     bool intersects(const Rect& r) const {
         return intersects(r.left, r.top, r.right, r.bottom);
     }
 
-    bool intersect(float l, float t, float r, float b) {
-        Rect tmp(l, t, r, b);
-        intersectWith(tmp);
-        if (!tmp.isEmpty()) {
-            set(tmp);
-            return true;
-        }
-        return false;
+    /**
+     * This method is named 'doIntersect' instead of 'intersect' so as not to be confused with
+     * SkRect::intersect / android.graphics.Rect#intersect behavior, which do not modify the object
+     * if the intersection of the rects would be empty.
+     */
+    void doIntersect(float l, float t, float r, float b) {
+        left = std::max(left, l);
+        top = std::max(top, t);
+        right = std::min(right, r);
+        bottom = std::min(bottom, b);
     }
 
-    bool intersect(const Rect& r) {
-        return intersect(r.left, r.top, r.right, r.bottom);
+    void doIntersect(const Rect& r) {
+        doIntersect(r.left, r.top, r.right, r.bottom);
     }
 
     inline bool contains(float l, float t, float r, float b) const {
@@ -271,24 +278,6 @@
     void dump(const char* label = nullptr) const {
         ALOGD("%s[l=%f t=%f r=%f b=%f]", label ? label : "Rect", left, top, right, bottom);
     }
-
-private:
-    void intersectWith(Rect& tmp) const {
-        tmp.left = std::max(left, tmp.left);
-        tmp.top = std::max(top, tmp.top);
-        tmp.right = std::min(right, tmp.right);
-        tmp.bottom = std::min(bottom, tmp.bottom);
-    }
-
-    Rect intersectWith(float l, float t, float r, float b) const {
-        Rect tmp;
-        tmp.left = std::max(left, l);
-        tmp.top = std::max(top, t);
-        tmp.right = std::min(right, r);
-        tmp.bottom = std::min(bottom, b);
-        return tmp;
-    }
-
 }; // class Rect
 
 }; // namespace uirenderer
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index ddc7ecd..bf1b4d0 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -725,7 +725,9 @@
 void RenderNode::issueDrawShadowOperation(const Matrix4& transformFromParent, T& handler) {
     if (properties().getAlpha() <= 0.0f
             || properties().getOutline().getAlpha() <= 0.0f
-            || !properties().getOutline().getPath()) {
+            || !properties().getOutline().getPath()
+            || properties().getScaleX() == 0
+            || properties().getScaleY() == 0) {
         // no shadow to draw
         return;
     }
@@ -915,7 +917,10 @@
     const bool useViewProperties = (!mLayer || drawLayer);
     if (useViewProperties) {
         const Outline& outline = properties().getOutline();
-        if (properties().getAlpha() <= 0 || (outline.getShouldClip() && outline.isEmpty())) {
+        if (properties().getAlpha() <= 0
+                || (outline.getShouldClip() && outline.isEmpty())
+                || properties().getScaleX() == 0
+                || properties().getScaleY() == 0) {
             DISPLAY_LIST_LOGD("%*sRejected display list (%p, %s)", handler.level() * 2, "",
                     this, getName());
             return;
diff --git a/libs/hwui/RenderProperties.cpp b/libs/hwui/RenderProperties.cpp
index ad74bff..ce1bd6a 100644
--- a/libs/hwui/RenderProperties.cpp
+++ b/libs/hwui/RenderProperties.cpp
@@ -52,11 +52,8 @@
 
 bool LayerProperties::setFromPaint(const SkPaint* paint) {
     bool changed = false;
-    SkXfermode::Mode mode;
-    int alpha;
-    OpenGLRenderer::getAlphaAndModeDirect(paint, &alpha, &mode);
-    changed |= setAlpha(static_cast<uint8_t>(alpha));
-    changed |= setXferMode(mode);
+    changed |= setAlpha(static_cast<uint8_t>(PaintUtils::getAlphaDirect(paint)));
+    changed |= setXferMode(PaintUtils::getXfermodeDirect(paint));
     changed |= setColorFilter(paint ? paint->getColorFilter() : nullptr);
     return changed;
 }
diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h
index 71589c8..f824cc0 100644
--- a/libs/hwui/RenderProperties.h
+++ b/libs/hwui/RenderProperties.h
@@ -549,7 +549,7 @@
         if (flags & CLIP_TO_BOUNDS) {
             outRect->set(0, 0, getWidth(), getHeight());
             if (flags & CLIP_TO_CLIP_BOUNDS) {
-                outRect->intersect(mPrimitiveFields.mClipBounds);
+                outRect->doIntersect(mPrimitiveFields.mClipBounds);
             }
         } else {
             outRect->set(mPrimitiveFields.mClipBounds);
diff --git a/libs/hwui/ShadowTessellator.cpp b/libs/hwui/ShadowTessellator.cpp
index 2209365..eb0fa74 100644
--- a/libs/hwui/ShadowTessellator.cpp
+++ b/libs/hwui/ShadowTessellator.cpp
@@ -73,8 +73,8 @@
     }
 
 #if DEBUG_SHADOW
-    ALOGD("light center %f %f %f",
-            adjustedLightCenter.x, adjustedLightCenter.y, adjustedLightCenter.z);
+    ALOGD("light center %f %f %f %d",
+            adjustedLightCenter.x, adjustedLightCenter.y, adjustedLightCenter.z, lightRadius);
 #endif
 
     // light position (because it's in local space) needs to compensate for receiver transform
diff --git a/libs/hwui/Snapshot.cpp b/libs/hwui/Snapshot.cpp
index 4d60b8d..0a58f4b 100644
--- a/libs/hwui/Snapshot.cpp
+++ b/libs/hwui/Snapshot.cpp
@@ -44,7 +44,7 @@
  * Copies the specified snapshot/ The specified snapshot is stored as
  * the previous snapshot.
  */
-Snapshot::Snapshot(const sp<Snapshot>& s, int saveFlags)
+Snapshot::Snapshot(Snapshot* s, int saveFlags)
         : flags(0)
         , previous(s)
         , layer(s->layer)
@@ -148,7 +148,7 @@
     const Snapshot* current = this;
     do {
         snapshotList.push(current);
-        current = current->previous.get();
+        current = current->previous;
     } while (current);
 
     // traverse the list, adding in each transform that contributes to the total transform
@@ -240,7 +240,7 @@
 
 void Snapshot::dump() const {
     ALOGD("Snapshot %p, flags %x, prev %p, height %d, ignored %d, hasComplexClip %d",
-            this, flags, previous.get(), getViewportHeight(), isIgnored(), !mClipArea->isSimple());
+            this, flags, previous, getViewportHeight(), isIgnored(), !mClipArea->isSimple());
     const Rect& clipRect(mClipArea->getClipRect());
     ALOGD("  ClipRect %.1f %.1f %.1f %.1f, clip simple %d",
             clipRect.left, clipRect.top, clipRect.right, clipRect.bottom, mClipArea->isSimple());
diff --git a/libs/hwui/Snapshot.h b/libs/hwui/Snapshot.h
index cf8f11c..aeeda96 100644
--- a/libs/hwui/Snapshot.h
+++ b/libs/hwui/Snapshot.h
@@ -83,11 +83,11 @@
  * Each snapshot has a link to a previous snapshot, indicating the previous
  * state of the renderer.
  */
-class Snapshot: public LightRefBase<Snapshot> {
+class Snapshot {
 public:
 
     Snapshot();
-    Snapshot(const sp<Snapshot>& s, int saveFlags);
+    Snapshot(Snapshot* s, int saveFlags);
 
     /**
      * Various flags set on ::flags.
@@ -229,7 +229,7 @@
     /**
      * Previous snapshot.
      */
-    sp<Snapshot> previous;
+    Snapshot* previous;
 
     /**
      * A pointer to the currently active layer.
diff --git a/libs/hwui/SpotShadow.cpp b/libs/hwui/SpotShadow.cpp
index 9b0a1aa..bdce73c 100644
--- a/libs/hwui/SpotShadow.cpp
+++ b/libs/hwui/SpotShadow.cpp
@@ -1051,7 +1051,7 @@
  */
 void SpotShadow::dumpPolygon(const Vector3* poly, int polyLength, const char* polyName) {
     for (int i = 0; i < polyLength; i++) {
-        ALOGD("polygon %s i %d x %f y %f", polyName, i, poly[i].x, poly[i].y);
+        ALOGD("polygon %s i %d x %f y %f z %f", polyName, i, poly[i].x, poly[i].y, poly[i].z);
     }
 }
 
diff --git a/libs/hwui/font/Font.cpp b/libs/hwui/font/Font.cpp
index fb0753b..d680f99 100644
--- a/libs/hwui/font/Font.cpp
+++ b/libs/hwui/font/Font.cpp
@@ -61,8 +61,6 @@
 }
 
 Font::~Font() {
-    mState->removeFont(this);
-
     for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
         delete mCachedGlyphs.valueAt(i);
     }
diff --git a/libs/hwui/renderstate/RenderState.cpp b/libs/hwui/renderstate/RenderState.cpp
index c5126de..dfa70ac 100644
--- a/libs/hwui/renderstate/RenderState.cpp
+++ b/libs/hwui/renderstate/RenderState.cpp
@@ -208,7 +208,7 @@
 // Render
 ///////////////////////////////////////////////////////////////////////////////
 
-void RenderState::render(const Glop& glop) {
+void RenderState::render(const Glop& glop, const Matrix4& orthoMatrix) {
     const Glop::Mesh& mesh = glop.mesh;
     const Glop::Mesh::Vertices& vertices = mesh.vertices;
     const Glop::Mesh::Indices& indices = mesh.indices;
@@ -223,7 +223,7 @@
         fill.program->setColor(fill.color);
     }
 
-    fill.program->set(glop.transform.ortho,
+    fill.program->set(orthoMatrix,
             glop.transform.modelView,
             glop.transform.meshTransform(),
             glop.transform.transformFlags & TransformFlags::OffsetByFudgeFactor);
diff --git a/libs/hwui/renderstate/RenderState.h b/libs/hwui/renderstate/RenderState.h
index 4fd792c..9ae0845 100644
--- a/libs/hwui/renderstate/RenderState.h
+++ b/libs/hwui/renderstate/RenderState.h
@@ -84,7 +84,7 @@
     // more thinking...
     void postDecStrong(VirtualLightRefBase* object);
 
-    void render(const Glop& glop);
+    void render(const Glop& glop, const Matrix4& orthoMatrix);
 
     AssetAtlas& assetAtlas() { return mAssetAtlas; }
     Blend& blend() { return *mBlend; }
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 9dc5b45..38f6e53 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -62,7 +62,7 @@
         , mAnimationContext(contextFactory->createAnimationContext(mRenderThread.timeLord()))
         , mJankTracker(thread.timeLord().frameIntervalNanos())
         , mProfiler(mFrames)
-        , mContentOverdrawProtectionBounds(0, 0, 0, 0) {
+        , mContentDrawBounds(0, 0, 0, 0) {
     mRenderNodes.emplace_back(rootRenderNode);
     mRenderThread.renderState().registerCanvasContext(this);
     mProfiler.setDensity(mRenderThread.mainDisplayInfo().density);
@@ -309,7 +309,7 @@
     Rect outBounds;
     // It there are multiple render nodes, they are as follows:
     // #0 - backdrop
-    // #1 - content (with - and clipped to - bounds mContentOverdrawProtectionBounds)
+    // #1 - content (positioned at (0,0) and clipped to - its bounds mContentDrawBounds)
     // #2 - frame
     // Usually the backdrop cannot be seen since it will be entirely covered by the content. While
     // resizing however it might become partially visible. The following render loop will crop the
@@ -317,66 +317,72 @@
     // against the backdrop (since that indicates a shrinking of the window) and then the frame
     // around everything.
     // The bounds of the backdrop against which the content should be clipped.
-    Rect backdropBounds = mContentOverdrawProtectionBounds;
+    Rect backdropBounds = mContentDrawBounds;
+    // Usually the contents bounds should be mContentDrawBounds - however - we will
+    // move it towards the fixed edge to give it a more stable appearance (for the moment).
+    Rect contentBounds;
     // If there is no content bounds we ignore the layering as stated above and start with 2.
-    int layer = mContentOverdrawProtectionBounds.isEmpty() ? 2 : 0;
+    int layer = (mContentDrawBounds.isEmpty() || mRenderNodes.size() <= 2) ? 2 : 0;
     // Draw all render nodes. Note that
     for (const sp<RenderNode>& node : mRenderNodes) {
         if (layer == 0) { // Backdrop.
-            // Draw the backdrop clipped to the inverse content bounds.
+            // Draw the backdrop clipped to the inverse content bounds, but assume that the content
+            // was moved to the upper left corner.
             const RenderProperties& properties = node->properties();
             Rect targetBounds(properties.getLeft(), properties.getTop(),
                               properties.getRight(), properties.getBottom());
+            // Move the content bounds towards the fixed corner of the backdrop.
+            const int x = targetBounds.left;
+            const int y = targetBounds.top;
+            contentBounds.set(x, y, x + mContentDrawBounds.getWidth(),
+                                    y + mContentDrawBounds.getHeight());
             // Remember the intersection of the target bounds and the intersection bounds against
             // which we have to crop the content.
-            backdropBounds.intersect(targetBounds);
+            backdropBounds.set(x, y, x + backdropBounds.getWidth(), y + backdropBounds.getHeight());
+            backdropBounds.doIntersect(targetBounds);
             // Check if we have to draw something on the left side ...
-            if (targetBounds.left < mContentOverdrawProtectionBounds.left) {
+            if (targetBounds.left < contentBounds.left) {
                 mCanvas->save(SkCanvas::kClip_SaveFlag);
                 if (mCanvas->clipRect(targetBounds.left, targetBounds.top,
-                                      mContentOverdrawProtectionBounds.left, targetBounds.bottom,
+                                      contentBounds.left, targetBounds.bottom,
                                       SkRegion::kIntersect_Op)) {
                     mCanvas->drawRenderNode(node.get(), outBounds);
                 }
                 // Reduce the target area by the area we have just painted.
-                targetBounds.left = std::min(mContentOverdrawProtectionBounds.left,
-                                             targetBounds.right);
+                targetBounds.left = std::min(contentBounds.left, targetBounds.right);
                 mCanvas->restore();
             }
             // ... or on the right side ...
-            if (targetBounds.right > mContentOverdrawProtectionBounds.right &&
+            if (targetBounds.right > contentBounds.right &&
                 !targetBounds.isEmpty()) {
                 mCanvas->save(SkCanvas::kClip_SaveFlag);
-                if (mCanvas->clipRect(mContentOverdrawProtectionBounds.right, targetBounds.top,
+                if (mCanvas->clipRect(contentBounds.right, targetBounds.top,
                                       targetBounds.right, targetBounds.bottom,
                                       SkRegion::kIntersect_Op)) {
                     mCanvas->drawRenderNode(node.get(), outBounds);
                 }
                 // Reduce the target area by the area we have just painted.
-                targetBounds.right = std::max(targetBounds.left,
-                                              mContentOverdrawProtectionBounds.right);
+                targetBounds.right = std::max(targetBounds.left, contentBounds.right);
                 mCanvas->restore();
             }
             // ... or at the top ...
-            if (targetBounds.top < mContentOverdrawProtectionBounds.top &&
+            if (targetBounds.top < contentBounds.top &&
                 !targetBounds.isEmpty()) {
                 mCanvas->save(SkCanvas::kClip_SaveFlag);
                 if (mCanvas->clipRect(targetBounds.left, targetBounds.top, targetBounds.right,
-                                      mContentOverdrawProtectionBounds.top,
+                                      contentBounds.top,
                                       SkRegion::kIntersect_Op)) {
                     mCanvas->drawRenderNode(node.get(), outBounds);
                 }
                 // Reduce the target area by the area we have just painted.
-                targetBounds.top = std::min(mContentOverdrawProtectionBounds.top,
-                                            targetBounds.bottom);
+                targetBounds.top = std::min(contentBounds.top, targetBounds.bottom);
                 mCanvas->restore();
             }
             // ... or at the bottom.
-            if (targetBounds.bottom > mContentOverdrawProtectionBounds.bottom &&
+            if (targetBounds.bottom > contentBounds.bottom &&
                 !targetBounds.isEmpty()) {
                 mCanvas->save(SkCanvas::kClip_SaveFlag);
-                if (mCanvas->clipRect(targetBounds.left,
-                                      mContentOverdrawProtectionBounds.bottom, targetBounds.right,
+                if (mCanvas->clipRect(targetBounds.left, contentBounds.bottom, targetBounds.right,
                                       targetBounds.bottom, SkRegion::kIntersect_Op)) {
                     mCanvas->drawRenderNode(node.get(), outBounds);
                 }
@@ -384,10 +390,17 @@
             }
         } else if (layer == 1) { // Content
             // It gets cropped against the bounds of the backdrop to stay inside.
-            mCanvas->save(SkCanvas::kClip_SaveFlag);
-            if (mCanvas->clipRect(backdropBounds.left, backdropBounds.top,
-                                  backdropBounds.right, backdropBounds.bottom,
-                                  SkRegion::kIntersect_Op)) {
+            mCanvas->save(SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag);
+
+            // We shift and clip the content to match its final location in the window.
+            const float left = mContentDrawBounds.left;
+            const float top = mContentDrawBounds.top;
+            const float dx = backdropBounds.left - left;
+            const float dy = backdropBounds.top - top;
+            const float width = backdropBounds.getWidth();
+            const float height = backdropBounds.getHeight();
+            mCanvas->translate(dx, dy);
+            if (mCanvas->clipRect(left, top, left + width, top + height, SkRegion::kIntersect_Op)) {
                 mCanvas->drawRenderNode(node.get(), outBounds);
             }
             mCanvas->restore();
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 1c3845c..e0cbabd 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -126,8 +126,8 @@
                 mRenderNodes.end());
     }
 
-    void setContentOverdrawProtectionBounds(int left, int top, int right, int bottom) {
-        mContentOverdrawProtectionBounds.set(left, top, right, bottom);
+    void setContentDrawBounds(int left, int top, int right, int bottom) {
+        mContentDrawBounds.set(left, top, right, bottom);
     }
 
 private:
@@ -167,7 +167,7 @@
     std::set<RenderNode*> mPrefetechedLayers;
 
     // Stores the bounds of the main content.
-    Rect mContentOverdrawProtectionBounds;
+    Rect mContentDrawBounds;
 };
 
 } /* namespace renderthread */
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index f43a769..26aae90 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -529,15 +529,14 @@
     staticPostAndWait(task);
 }
 
-CREATE_BRIDGE5(setContentOverdrawProtectionBounds, CanvasContext* context, int left, int top,
+CREATE_BRIDGE5(setContentDrawBounds, CanvasContext* context, int left, int top,
         int right, int bottom) {
-    args->context->setContentOverdrawProtectionBounds(args->left, args->top, args->right,
-                                                      args->bottom);
+    args->context->setContentDrawBounds(args->left, args->top, args->right, args->bottom);
     return nullptr;
 }
 
-void RenderProxy::setContentOverdrawProtectionBounds(int left, int top, int right, int bottom) {
-    SETUP_TASK(setContentOverdrawProtectionBounds);
+void RenderProxy::setContentDrawBounds(int left, int top, int right, int bottom) {
+    SETUP_TASK(setContentDrawBounds);
     args->context = mContext;
     args->left = left;
     args->top = top;
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index 046f24a..d1b62f1 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -109,7 +109,7 @@
     ANDROID_API void addRenderNode(RenderNode* node, bool placeFront);
     ANDROID_API void removeRenderNode(RenderNode* node);
     ANDROID_API void drawRenderNode(RenderNode* node);
-    ANDROID_API void setContentOverdrawProtectionBounds(int left, int top, int right, int bottom);
+    ANDROID_API void setContentDrawBounds(int left, int top, int right, int bottom);
 
 private:
     RenderThread& mRenderThread;
diff --git a/libs/hwui/tests/Benchmark.h b/libs/hwui/tests/Benchmark.h
new file mode 100644
index 0000000..e16310e
--- /dev/null
+++ b/libs/hwui/tests/Benchmark.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef TESTS_BENCHMARK_H
+#define TESTS_BENCHMARK_H
+
+#include <string>
+#include <vector>
+
+namespace android {
+namespace uirenderer {
+
+struct BenchmarkOptions {
+    int count;
+};
+
+typedef void (*BenchmarkFunctor)(const BenchmarkOptions&);
+
+struct BenchmarkInfo {
+    std::string name;
+    std::string description;
+    BenchmarkFunctor functor;
+};
+
+class Benchmark {
+public:
+    Benchmark(const BenchmarkInfo& info) {
+        registerBenchmark(info);
+    }
+
+private:
+    Benchmark() = delete;
+    Benchmark(const Benchmark&) = delete;
+    Benchmark& operator=(const Benchmark&) = delete;
+
+    static void registerBenchmark(const BenchmarkInfo& info);
+};
+
+} /* namespace uirenderer */
+} /* namespace android */
+
+#endif /* TESTS_BENCHMARK_H */
diff --git a/libs/hwui/tests/TestContext.cpp b/libs/hwui/tests/TestContext.cpp
index cebe765..ba763a8 100644
--- a/libs/hwui/tests/TestContext.cpp
+++ b/libs/hwui/tests/TestContext.cpp
@@ -22,16 +22,35 @@
 
 static const int IDENT_DISPLAYEVENT = 1;
 
-static DisplayInfo getBuiltInDisplay() {
+static android::DisplayInfo DUMMY_DISPLAY {
+    1080, //w
+    1920, //h
+    320.0, // xdpi
+    320.0, // ydpi
+    60.0, // fps
+    2.0, // density
+    0, // orientation
+    false, // secure?
+    0, // appVsyncOffset
+    0, // presentationDeadline
+    0, // colorTransform
+};
+
+DisplayInfo getBuiltInDisplay() {
+#if !HWUI_NULL_GPU
     DisplayInfo display;
     sp<IBinder> dtoken(SurfaceComposerClient::getBuiltInDisplay(
             ISurfaceComposer::eDisplayIdMain));
     status_t status = SurfaceComposerClient::getDisplayInfo(dtoken, &display);
     LOG_ALWAYS_FATAL_IF(status, "Failed to get display info\n");
     return display;
+#else
+    return DUMMY_DISPLAY;
+#endif
 }
 
-android::DisplayInfo gDisplay = getBuiltInDisplay();
+// Initialize to a dummy default
+android::DisplayInfo gDisplay = DUMMY_DISPLAY;
 
 TestContext::TestContext() {
     mLooper = new Looper(true);
diff --git a/libs/hwui/tests/TestContext.h b/libs/hwui/tests/TestContext.h
index 7b30fc1..2bbe5df 100644
--- a/libs/hwui/tests/TestContext.h
+++ b/libs/hwui/tests/TestContext.h
@@ -32,6 +32,8 @@
 extern DisplayInfo gDisplay;
 #define dp(x) ((x) * android::uirenderer::test::gDisplay.density)
 
+DisplayInfo getBuiltInDisplay();
+
 class TestContext {
 public:
     TestContext();
diff --git a/libs/hwui/tests/TreeContentAnimation.cpp b/libs/hwui/tests/TreeContentAnimation.cpp
new file mode 100644
index 0000000..a59261c
--- /dev/null
+++ b/libs/hwui/tests/TreeContentAnimation.cpp
@@ -0,0 +1,385 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <cutils/log.h>
+#include <gui/Surface.h>
+#include <ui/PixelFormat.h>
+
+#include <AnimationContext.h>
+#include <DisplayListCanvas.h>
+#include <RenderNode.h>
+#include <renderthread/RenderProxy.h>
+#include <renderthread/RenderTask.h>
+
+#include "Benchmark.h"
+#include "TestContext.h"
+
+#include "protos/hwui.pb.h"
+
+#include <stdio.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <vector>
+
+using namespace android;
+using namespace android::uirenderer;
+using namespace android::uirenderer::renderthread;
+using namespace android::uirenderer::test;
+
+class ContextFactory : public IContextFactory {
+public:
+    virtual AnimationContext* createAnimationContext(renderthread::TimeLord& clock) override {
+        return new AnimationContext(clock);
+    }
+};
+
+static DisplayListCanvas* startRecording(RenderNode* node) {
+    DisplayListCanvas* renderer = new DisplayListCanvas(
+            node->stagingProperties().getWidth(), node->stagingProperties().getHeight());
+    return renderer;
+}
+
+static void endRecording(DisplayListCanvas* renderer, RenderNode* node) {
+    node->setStagingDisplayList(renderer->finishRecording());
+    delete renderer;
+}
+
+class TreeContentAnimation {
+public:
+    virtual ~TreeContentAnimation() {}
+    int frameCount = 150;
+    virtual int getFrameCount() { return frameCount; }
+    virtual void setFrameCount(int fc) {
+        if (fc > 0) {
+            frameCount = fc;
+        }
+    }
+    virtual void createContent(int width, int height, DisplayListCanvas* renderer) = 0;
+    virtual void doFrame(int frameNr) = 0;
+
+    template <class T>
+    static void run(const BenchmarkOptions& opts) {
+        // Switch to the real display
+        gDisplay = getBuiltInDisplay();
+
+        T animation;
+        animation.setFrameCount(opts.count);
+
+        TestContext testContext;
+
+        // create the native surface
+        const int width = gDisplay.w;
+        const int height = gDisplay.h;
+        sp<Surface> surface = testContext.surface();
+
+        RenderNode* rootNode = new RenderNode();
+        rootNode->incStrong(nullptr);
+        rootNode->mutateStagingProperties().setLeftTopRightBottom(0, 0, width, height);
+        rootNode->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+        rootNode->mutateStagingProperties().setClipToBounds(false);
+        rootNode->setPropertyFieldsDirty(RenderNode::GENERIC);
+
+        ContextFactory factory;
+        std::unique_ptr<RenderProxy> proxy(new RenderProxy(false, rootNode, &factory));
+        proxy->loadSystemProperties();
+        proxy->initialize(surface);
+        float lightX = width / 2.0;
+        proxy->setup(width, height, dp(800.0f), 255 * 0.075, 255 * 0.15);
+        proxy->setLightCenter((Vector3){lightX, dp(-200.0f), dp(800.0f)});
+
+        android::uirenderer::Rect DUMMY;
+
+        DisplayListCanvas* renderer = startRecording(rootNode);
+        animation.createContent(width, height, renderer);
+        endRecording(renderer, rootNode);
+
+        // Do a few cold runs then reset the stats so that the caches are all hot
+        for (int i = 0; i < 3; i++) {
+            testContext.waitForVsync();
+            proxy->syncAndDrawFrame();
+        }
+        proxy->resetProfileInfo();
+
+        for (int i = 0; i < animation.getFrameCount(); i++) {
+            testContext.waitForVsync();
+
+            ATRACE_NAME("UI-Draw Frame");
+            nsecs_t vsync = systemTime(CLOCK_MONOTONIC);
+            UiFrameInfoBuilder(proxy->frameInfo())
+                    .setVsync(vsync, vsync);
+            animation.doFrame(i);
+            proxy->syncAndDrawFrame();
+        }
+
+        proxy->dumpProfileInfo(STDOUT_FILENO, 0);
+        rootNode->decStrong(nullptr);
+    }
+};
+
+class ShadowGridAnimation : public TreeContentAnimation {
+public:
+    std::vector< sp<RenderNode> > cards;
+    void createContent(int width, int height, DisplayListCanvas* renderer) override {
+        renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
+        renderer->insertReorderBarrier(true);
+
+        for (int x = dp(16); x < (width - dp(116)); x += dp(116)) {
+            for (int y = dp(16); y < (height - dp(116)); y += dp(116)) {
+                sp<RenderNode> card = createCard(x, y, dp(100), dp(100));
+                renderer->drawRenderNode(card.get());
+                cards.push_back(card);
+            }
+        }
+
+        renderer->insertReorderBarrier(false);
+    }
+    void doFrame(int frameNr) override {
+        int curFrame = frameNr % 150;
+        for (size_t ci = 0; ci < cards.size(); ci++) {
+            cards[ci]->mutateStagingProperties().setTranslationX(curFrame);
+            cards[ci]->mutateStagingProperties().setTranslationY(curFrame);
+            cards[ci]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+        }
+    }
+private:
+    sp<RenderNode> createCard(int x, int y, int width, int height) {
+        sp<RenderNode> node = new RenderNode();
+        node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
+        node->mutateStagingProperties().setElevation(dp(16));
+        node->mutateStagingProperties().mutableOutline().setRoundRect(0, 0, width, height, dp(10), 1);
+        node->mutateStagingProperties().mutableOutline().setShouldClip(true);
+        node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y | RenderNode::Z);
+
+        DisplayListCanvas* renderer = startRecording(node.get());
+        renderer->drawColor(0xFFEEEEEE, SkXfermode::kSrcOver_Mode);
+        endRecording(renderer, node.get());
+        return node;
+    }
+};
+static Benchmark _ShadowGrid(BenchmarkInfo{
+    "shadowgrid",
+    "A grid of rounded rects that cast a shadow. Simplified scenario of an "
+    "Android TV-style launcher interface. High CPU/GPU load.",
+    TreeContentAnimation::run<ShadowGridAnimation>
+});
+
+class ShadowGrid2Animation : public TreeContentAnimation {
+public:
+    std::vector< sp<RenderNode> > cards;
+    void createContent(int width, int height, DisplayListCanvas* renderer) override {
+        renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
+        renderer->insertReorderBarrier(true);
+
+        for (int x = dp(8); x < (width - dp(58)); x += dp(58)) {
+            for (int y = dp(8); y < (height - dp(58)); y += dp(58)) {
+                sp<RenderNode> card = createCard(x, y, dp(50), dp(50));
+                renderer->drawRenderNode(card.get());
+                cards.push_back(card);
+            }
+        }
+
+        renderer->insertReorderBarrier(false);
+    }
+    void doFrame(int frameNr) override {
+        int curFrame = frameNr % 150;
+        for (size_t ci = 0; ci < cards.size(); ci++) {
+            cards[ci]->mutateStagingProperties().setTranslationX(curFrame);
+            cards[ci]->mutateStagingProperties().setTranslationY(curFrame);
+            cards[ci]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+        }
+    }
+private:
+    sp<RenderNode> createCard(int x, int y, int width, int height) {
+        sp<RenderNode> node = new RenderNode();
+        node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
+        node->mutateStagingProperties().setElevation(dp(16));
+        node->mutateStagingProperties().mutableOutline().setRoundRect(0, 0, width, height, dp(6), 1);
+        node->mutateStagingProperties().mutableOutline().setShouldClip(true);
+        node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y | RenderNode::Z);
+
+        DisplayListCanvas* renderer = startRecording(node.get());
+        renderer->drawColor(0xFFEEEEEE, SkXfermode::kSrcOver_Mode);
+        endRecording(renderer, node.get());
+        return node;
+    }
+};
+static Benchmark _ShadowGrid2(BenchmarkInfo{
+    "shadowgrid2",
+    "A dense grid of rounded rects that cast a shadow. This is a higher CPU load "
+    "variant of shadowgrid. Very high CPU load, high GPU load.",
+    TreeContentAnimation::run<ShadowGrid2Animation>
+});
+
+class RectGridAnimation : public TreeContentAnimation {
+public:
+    sp<RenderNode> card;
+    void createContent(int width, int height, DisplayListCanvas* renderer) override {
+        renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
+        renderer->insertReorderBarrier(true);
+
+        card = createCard(40, 40, 200, 200);
+        renderer->drawRenderNode(card.get());
+
+        renderer->insertReorderBarrier(false);
+    }
+    void doFrame(int frameNr) override {
+        int curFrame = frameNr % 150;
+        card->mutateStagingProperties().setTranslationX(curFrame);
+        card->mutateStagingProperties().setTranslationY(curFrame);
+        card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+    }
+private:
+    sp<RenderNode> createCard(int x, int y, int width, int height) {
+        sp<RenderNode> node = new RenderNode();
+        node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
+        node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+
+        DisplayListCanvas* renderer = startRecording(node.get());
+        renderer->drawColor(0xFFFF00FF, SkXfermode::kSrcOver_Mode);
+
+        SkRegion region;
+        for (int xOffset = 0; xOffset < width; xOffset+=2) {
+            for (int yOffset = 0; yOffset < height; yOffset+=2) {
+                region.op(xOffset, yOffset, xOffset + 1, yOffset + 1, SkRegion::kUnion_Op);
+            }
+        }
+
+        SkPaint paint;
+        paint.setColor(0xff00ffff);
+        renderer->drawRegion(region, paint);
+
+        endRecording(renderer, node.get());
+        return node;
+    }
+};
+static Benchmark _RectGrid(BenchmarkInfo{
+    "rectgrid",
+    "A dense grid of 1x1 rects that should visually look like a single rect. "
+    "Low CPU/GPU load.",
+    TreeContentAnimation::run<RectGridAnimation>
+});
+
+class OvalAnimation : public TreeContentAnimation {
+public:
+    sp<RenderNode> card;
+    void createContent(int width, int height, DisplayListCanvas* renderer) override {
+        renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
+        renderer->insertReorderBarrier(true);
+
+        card = createCard(40, 40, 400, 400);
+        renderer->drawRenderNode(card.get());
+
+        renderer->insertReorderBarrier(false);
+    }
+
+    void doFrame(int frameNr) override {
+        int curFrame = frameNr % 150;
+        card->mutateStagingProperties().setTranslationX(curFrame);
+        card->mutateStagingProperties().setTranslationY(curFrame);
+        card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+    }
+private:
+    sp<RenderNode> createCard(int x, int y, int width, int height) {
+        sp<RenderNode> node = new RenderNode();
+        node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
+        node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+
+        DisplayListCanvas* renderer = startRecording(node.get());
+
+        SkPaint paint;
+        paint.setAntiAlias(true);
+        paint.setColor(0xFF000000);
+        renderer->drawOval(0, 0, width, height, paint);
+
+        endRecording(renderer, node.get());
+        return node;
+    }
+};
+static Benchmark _Oval(BenchmarkInfo{
+    "oval",
+    "Draws 1 oval.",
+    TreeContentAnimation::run<OvalAnimation>
+});
+
+class PartialDamageTest : public TreeContentAnimation {
+public:
+    std::vector< sp<RenderNode> > cards;
+    void createContent(int width, int height, DisplayListCanvas* renderer) override {
+        static SkColor COLORS[] = {
+                0xFFF44336,
+                0xFF9C27B0,
+                0xFF2196F3,
+                0xFF4CAF50,
+        };
+
+        renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
+
+        for (int x = dp(16); x < (width - dp(116)); x += dp(116)) {
+            for (int y = dp(16); y < (height - dp(116)); y += dp(116)) {
+                sp<RenderNode> card = createCard(x, y, dp(100), dp(100),
+                        COLORS[static_cast<int>((y / dp(116))) % 4]);
+                renderer->drawRenderNode(card.get());
+                cards.push_back(card);
+            }
+        }
+    }
+    void doFrame(int frameNr) override {
+        int curFrame = frameNr % 150;
+        cards[0]->mutateStagingProperties().setTranslationX(curFrame);
+        cards[0]->mutateStagingProperties().setTranslationY(curFrame);
+        cards[0]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+
+        DisplayListCanvas* renderer = startRecording(cards[0].get());
+        renderer->drawColor(interpolateColor(curFrame / 150.0f, 0xFFF44336, 0xFFF8BBD0),
+                SkXfermode::kSrcOver_Mode);
+        endRecording(renderer, cards[0].get());
+    }
+
+    static SkColor interpolateColor(float fraction, SkColor start, SkColor end) {
+        int startA = (start >> 24) & 0xff;
+        int startR = (start >> 16) & 0xff;
+        int startG = (start >> 8) & 0xff;
+        int startB = start & 0xff;
+
+        int endA = (end >> 24) & 0xff;
+        int endR = (end >> 16) & 0xff;
+        int endG = (end >> 8) & 0xff;
+        int endB = end & 0xff;
+
+        return (int)((startA + (int)(fraction * (endA - startA))) << 24) |
+                (int)((startR + (int)(fraction * (endR - startR))) << 16) |
+                (int)((startG + (int)(fraction * (endG - startG))) << 8) |
+                (int)((startB + (int)(fraction * (endB - startB))));
+    }
+private:
+    sp<RenderNode> createCard(int x, int y, int width, int height, SkColor color) {
+        sp<RenderNode> node = new RenderNode();
+        node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
+        node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+
+        DisplayListCanvas* renderer = startRecording(node.get());
+        renderer->drawColor(color, SkXfermode::kSrcOver_Mode);
+        endRecording(renderer, node.get());
+        return node;
+    }
+};
+static Benchmark _PartialDamage(BenchmarkInfo{
+    "partialdamage",
+    "Tests the partial invalidation path. Draws a grid of rects and animates 1 "
+    "of them, should be low CPU & GPU load if EGL_EXT_buffer_age or "
+    "EGL_KHR_partial_update is supported by the device & are enabled in hwui.",
+    TreeContentAnimation::run<PartialDamageTest>
+});
diff --git a/libs/hwui/tests/how_to_run.txt b/libs/hwui/tests/how_to_run.txt
index 85900ef..b051768f 100644
--- a/libs/hwui/tests/how_to_run.txt
+++ b/libs/hwui/tests/how_to_run.txt
@@ -2,16 +2,4 @@
     adb push $OUT/data/local/tmp/hwuitest /data/local/tmp/hwuitest &&
     adb shell /data/local/tmp/hwuitest
 
-
-Command arguments:
-hwuitest [testname]
-
-Default test is 'shadowgrid'
-
-List of tests:
-
-shadowgrid: creates a grid of rounded rects that cast shadows, high CPU & GPU load
-
-rectgrid: creates a grid of 1x1 rects
-
-oval: draws 1 oval
+Pass --help to get help
diff --git a/libs/hwui/tests/main.cpp b/libs/hwui/tests/main.cpp
index 0bbf08c..aee84de 100644
--- a/libs/hwui/tests/main.cpp
+++ b/libs/hwui/tests/main.cpp
@@ -14,385 +14,181 @@
  * limitations under the License.
  */
 
-#include <cutils/log.h>
-#include <gui/Surface.h>
-#include <ui/PixelFormat.h>
-
-#include <AnimationContext.h>
-#include <DisplayListCanvas.h>
-#include <RenderNode.h>
-#include <renderthread/RenderProxy.h>
-#include <renderthread/RenderTask.h>
-
-#include "TestContext.h"
+#include "Benchmark.h"
 
 #include "protos/hwui.pb.h"
 
+#include <getopt.h>
 #include <stdio.h>
+#include <string>
 #include <unistd.h>
+#include <unordered_map>
+#include <vector>
 
 using namespace android;
 using namespace android::uirenderer;
-using namespace android::uirenderer::renderthread;
-using namespace android::uirenderer::test;
 
-class ContextFactory : public IContextFactory {
-public:
-    virtual AnimationContext* createAnimationContext(renderthread::TimeLord& clock) override {
-        return new AnimationContext(clock);
-    }
-};
-
-static DisplayListCanvas* startRecording(RenderNode* node) {
-    DisplayListCanvas* renderer = new DisplayListCanvas(
-            node->stagingProperties().getWidth(), node->stagingProperties().getHeight());
-    return renderer;
+// Not a static global because we need to force the map to be constructed
+// before we try to add things to it.
+std::unordered_map<std::string, BenchmarkInfo>& testMap() {
+    static std::unordered_map<std::string, BenchmarkInfo> testMap;
+    return testMap;
 }
 
-static void endRecording(DisplayListCanvas* renderer, RenderNode* node) {
-    node->setStagingDisplayList(renderer->finishRecording());
-    delete renderer;
+void Benchmark::registerBenchmark(const BenchmarkInfo& info) {
+    testMap()[info.name] = info;
 }
 
-class TreeContentAnimation {
-public:
-    virtual ~TreeContentAnimation() {}
-    int frameCount = 150;
-    virtual int getFrameCount() { return frameCount; }
-    virtual void setFrameCount(int fc) {
-        if (fc > 0) {
-            frameCount = fc;
-        }
-    }
-    virtual void createContent(int width, int height, DisplayListCanvas* renderer) = 0;
-    virtual void doFrame(int frameNr) = 0;
+static int gFrameCount = 150;
+static int gRepeatCount = 1;
+static std::vector<BenchmarkInfo> gRunTests;
 
-    template <class T>
-    static void run(int frameCount) {
-        T animation;
-        animation.setFrameCount(frameCount);
+static void printHelp() {
+    printf("\
+USAGE: hwuitest [OPTIONS] <TESTNAME>\n\
+\n\
+OPTIONS:\n\
+  -c, --count=NUM      NUM loops a test should run (example, number of frames)\n\
+  -r, --runs=NUM       Repeat the test(s) NUM times\n\
+  -h, --help           Display this help\n\
+  --list               List all tests\n\
+\n");
+}
 
-        TestContext testContext;
-
-        // create the native surface
-        const int width = gDisplay.w;
-        const int height = gDisplay.h;
-        sp<Surface> surface = testContext.surface();
-
-        RenderNode* rootNode = new RenderNode();
-        rootNode->incStrong(nullptr);
-        rootNode->mutateStagingProperties().setLeftTopRightBottom(0, 0, width, height);
-        rootNode->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
-        rootNode->mutateStagingProperties().setClipToBounds(false);
-        rootNode->setPropertyFieldsDirty(RenderNode::GENERIC);
-
-        ContextFactory factory;
-        std::unique_ptr<RenderProxy> proxy(new RenderProxy(false, rootNode, &factory));
-        proxy->loadSystemProperties();
-        proxy->initialize(surface);
-        float lightX = width / 2.0;
-        proxy->setup(width, height, dp(800.0f), 255 * 0.075, 255 * 0.15);
-        proxy->setLightCenter((Vector3){lightX, dp(-200.0f), dp(800.0f)});
-
-        android::uirenderer::Rect DUMMY;
-
-        DisplayListCanvas* renderer = startRecording(rootNode);
-        animation.createContent(width, height, renderer);
-        endRecording(renderer, rootNode);
-
-        // Do a few cold runs then reset the stats so that the caches are all hot
-        for (int i = 0; i < 3; i++) {
-            testContext.waitForVsync();
-            proxy->syncAndDrawFrame();
-        }
-        proxy->resetProfileInfo();
-
-        for (int i = 0; i < animation.getFrameCount(); i++) {
-            testContext.waitForVsync();
-
-            ATRACE_NAME("UI-Draw Frame");
-            nsecs_t vsync = systemTime(CLOCK_MONOTONIC);
-            UiFrameInfoBuilder(proxy->frameInfo())
-                    .setVsync(vsync, vsync);
-            animation.doFrame(i);
-            proxy->syncAndDrawFrame();
-        }
-
-        proxy->dumpProfileInfo(STDOUT_FILENO, 0);
-        rootNode->decStrong(nullptr);
-    }
-};
-
-class ShadowGridAnimation : public TreeContentAnimation {
-public:
-    std::vector< sp<RenderNode> > cards;
-    void createContent(int width, int height, DisplayListCanvas* renderer) override {
-        renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
-        renderer->insertReorderBarrier(true);
-
-        for (int x = dp(16); x < (width - dp(116)); x += dp(116)) {
-            for (int y = dp(16); y < (height - dp(116)); y += dp(116)) {
-                sp<RenderNode> card = createCard(x, y, dp(100), dp(100));
-                renderer->drawRenderNode(card.get());
-                cards.push_back(card);
+static void listTests() {
+    printf("Tests: \n");
+    for (auto&& test : testMap()) {
+        auto&& info = test.second;
+        const char* col1 = info.name.c_str();
+        int dlen = info.description.length();
+        const char* col2 = info.description.c_str();
+        // World's best line breaking algorithm.
+        do {
+            int toPrint = dlen;
+            if (toPrint > 50) {
+                char* found = (char*) memrchr(col2, ' ', 50);
+                if (found) {
+                    toPrint = found - col2;
+                } else {
+                    toPrint = 50;
+                }
             }
-        }
-
-        renderer->insertReorderBarrier(false);
-    }
-    void doFrame(int frameNr) override {
-        int curFrame = frameNr % 150;
-        for (size_t ci = 0; ci < cards.size(); ci++) {
-            cards[ci]->mutateStagingProperties().setTranslationX(curFrame);
-            cards[ci]->mutateStagingProperties().setTranslationY(curFrame);
-            cards[ci]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
-        }
-    }
-private:
-    sp<RenderNode> createCard(int x, int y, int width, int height) {
-        sp<RenderNode> node = new RenderNode();
-        node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
-        node->mutateStagingProperties().setElevation(dp(16));
-        node->mutateStagingProperties().mutableOutline().setRoundRect(0, 0, width, height, dp(10), 1);
-        node->mutateStagingProperties().mutableOutline().setShouldClip(true);
-        node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y | RenderNode::Z);
-
-        DisplayListCanvas* renderer = startRecording(node.get());
-        renderer->drawColor(0xFFEEEEEE, SkXfermode::kSrcOver_Mode);
-        endRecording(renderer, node.get());
-        return node;
-    }
-};
-
-class ShadowGrid2Animation : public TreeContentAnimation {
-public:
-    std::vector< sp<RenderNode> > cards;
-    void createContent(int width, int height, DisplayListCanvas* renderer) override {
-        renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
-        renderer->insertReorderBarrier(true);
-
-        for (int x = dp(8); x < (width - dp(58)); x += dp(58)) {
-            for (int y = dp(8); y < (height - dp(58)); y += dp(58)) {
-                sp<RenderNode> card = createCard(x, y, dp(50), dp(50));
-                renderer->drawRenderNode(card.get());
-                cards.push_back(card);
+            printf("%-20s %.*s\n", col1, toPrint, col2);
+            col1 = "";
+            col2 += toPrint;
+            dlen -= toPrint;
+            while (*col2 == ' ') {
+                col2++; dlen--;
             }
-        }
+        } while (dlen > 0);
+        printf("\n");
+    }
+}
 
-        renderer->insertReorderBarrier(false);
-    }
-    void doFrame(int frameNr) override {
-        int curFrame = frameNr % 150;
-        for (size_t ci = 0; ci < cards.size(); ci++) {
-            cards[ci]->mutateStagingProperties().setTranslationX(curFrame);
-            cards[ci]->mutateStagingProperties().setTranslationY(curFrame);
-            cards[ci]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
-        }
-    }
-private:
-    sp<RenderNode> createCard(int x, int y, int width, int height) {
-        sp<RenderNode> node = new RenderNode();
-        node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
-        node->mutateStagingProperties().setElevation(dp(16));
-        node->mutateStagingProperties().mutableOutline().setRoundRect(0, 0, width, height, dp(6), 1);
-        node->mutateStagingProperties().mutableOutline().setShouldClip(true);
-        node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y | RenderNode::Z);
-
-        DisplayListCanvas* renderer = startRecording(node.get());
-        renderer->drawColor(0xFFEEEEEE, SkXfermode::kSrcOver_Mode);
-        endRecording(renderer, node.get());
-        return node;
-    }
+static const struct option LONG_OPTIONS[] = {
+    { "frames", required_argument, nullptr, 'f' },
+    { "repeat", required_argument, nullptr, 'r' },
+    { "help", no_argument, nullptr, 'h' },
+    { "list", no_argument, nullptr, 'l' },
+    { 0, 0, 0, 0 }
 };
 
-class RectGridAnimation : public TreeContentAnimation {
-public:
-    sp<RenderNode> card;
-    void createContent(int width, int height, DisplayListCanvas* renderer) override {
-        renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
-        renderer->insertReorderBarrier(true);
+static const char* SHORT_OPTIONS = "c:r:h";
 
-        card = createCard(40, 40, 200, 200);
-        renderer->drawRenderNode(card.get());
+void parseOptions(int argc, char* argv[]) {
+    int c;
+    // temporary variable
+    int count;
+    bool error = false;
+    opterr = 0;
 
-        renderer->insertReorderBarrier(false);
-    }
-    void doFrame(int frameNr) override {
-        int curFrame = frameNr % 150;
-        card->mutateStagingProperties().setTranslationX(curFrame);
-        card->mutateStagingProperties().setTranslationY(curFrame);
-        card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
-    }
-private:
-    sp<RenderNode> createCard(int x, int y, int width, int height) {
-        sp<RenderNode> node = new RenderNode();
-        node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
-        node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+    while (true) {
 
-        DisplayListCanvas* renderer = startRecording(node.get());
-        renderer->drawColor(0xFFFF00FF, SkXfermode::kSrcOver_Mode);
+        /* getopt_long stores the option index here. */
+        int option_index = 0;
 
-        SkRegion region;
-        for (int xOffset = 0; xOffset < width; xOffset+=2) {
-            for (int yOffset = 0; yOffset < height; yOffset+=2) {
-                region.op(xOffset, yOffset, xOffset + 1, yOffset + 1, SkRegion::kUnion_Op);
+        c = getopt_long(argc, argv, SHORT_OPTIONS, LONG_OPTIONS, &option_index);
+
+        if (c == -1)
+            break;
+
+        switch (c) {
+        case 0:
+            // Option set a flag, don't need to do anything
+            // (although none of the current LONG_OPTIONS do this...)
+            break;
+
+        case 'l':
+            listTests();
+            exit(EXIT_SUCCESS);
+            break;
+
+        case 'c':
+            count = atoi(optarg);
+            if (!count) {
+                fprintf(stderr, "Invalid frames argument '%s'\n", optarg);
+                error = true;
+            } else {
+                gFrameCount = (count > 0 ? count : INT_MAX);
             }
-        }
+            break;
 
-        SkPaint paint;
-        paint.setColor(0xff00ffff);
-        renderer->drawRegion(region, paint);
-
-        endRecording(renderer, node.get());
-        return node;
-    }
-};
-
-class OvalAnimation : public TreeContentAnimation {
-public:
-    sp<RenderNode> card;
-    void createContent(int width, int height, DisplayListCanvas* renderer) override {
-        renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
-        renderer->insertReorderBarrier(true);
-
-        card = createCard(40, 40, 400, 400);
-        renderer->drawRenderNode(card.get());
-
-        renderer->insertReorderBarrier(false);
-    }
-
-    void doFrame(int frameNr) override {
-        int curFrame = frameNr % 150;
-        card->mutateStagingProperties().setTranslationX(curFrame);
-        card->mutateStagingProperties().setTranslationY(curFrame);
-        card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
-    }
-private:
-    sp<RenderNode> createCard(int x, int y, int width, int height) {
-        sp<RenderNode> node = new RenderNode();
-        node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
-        node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
-
-        DisplayListCanvas* renderer = startRecording(node.get());
-
-        SkPaint paint;
-        paint.setAntiAlias(true);
-        paint.setColor(0xFF000000);
-        renderer->drawOval(0, 0, width, height, paint);
-
-        endRecording(renderer, node.get());
-        return node;
-    }
-};
-
-class PartialInvalTest : public TreeContentAnimation {
-public:
-    std::vector< sp<RenderNode> > cards;
-    void createContent(int width, int height, DisplayListCanvas* renderer) override {
-        static SkColor COLORS[] = {
-                0xFFF44336,
-                0xFF9C27B0,
-                0xFF2196F3,
-                0xFF4CAF50,
-        };
-
-        renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
-
-        for (int x = dp(16); x < (width - dp(116)); x += dp(116)) {
-            for (int y = dp(16); y < (height - dp(116)); y += dp(116)) {
-                sp<RenderNode> card = createCard(x, y, dp(100), dp(100),
-                        COLORS[static_cast<int>((y / dp(116))) % 4]);
-                renderer->drawRenderNode(card.get());
-                cards.push_back(card);
+        case 'r':
+            count = atoi(optarg);
+            if (!count) {
+                fprintf(stderr, "Invalid repeat argument '%s'\n", optarg);
+                error = true;
+            } else {
+                gRepeatCount = (count > 0 ? count : INT_MAX);
             }
+            break;
+
+        case 'h':
+            printHelp();
+            exit(EXIT_SUCCESS);
+            break;
+
+        case '?':
+            fprintf(stderr, "Unrecognized option '%s'\n", argv[optind - 1]);
+            // fall-through
+        default:
+            error = true;
+            break;
         }
     }
-    void doFrame(int frameNr) override {
-        int curFrame = frameNr % 150;
-        cards[0]->mutateStagingProperties().setTranslationX(curFrame);
-        cards[0]->mutateStagingProperties().setTranslationY(curFrame);
-        cards[0]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
 
-        DisplayListCanvas* renderer = startRecording(cards[0].get());
-        renderer->drawColor(interpolateColor(curFrame / 150.0f, 0xFFF44336, 0xFFF8BBD0),
-                SkXfermode::kSrcOver_Mode);
-        endRecording(renderer, cards[0].get());
+    if (error) {
+        fprintf(stderr, "Try 'hwuitest --help' for more information.\n");
+        exit(EXIT_FAILURE);
     }
 
-    static SkColor interpolateColor(float fraction, SkColor start, SkColor end) {
-        int startA = (start >> 24) & 0xff;
-        int startR = (start >> 16) & 0xff;
-        int startG = (start >> 8) & 0xff;
-        int startB = start & 0xff;
-
-        int endA = (end >> 24) & 0xff;
-        int endR = (end >> 16) & 0xff;
-        int endG = (end >> 8) & 0xff;
-        int endB = end & 0xff;
-
-        return (int)((startA + (int)(fraction * (endA - startA))) << 24) |
-                (int)((startR + (int)(fraction * (endR - startR))) << 16) |
-                (int)((startG + (int)(fraction * (endG - startG))) << 8) |
-                (int)((startB + (int)(fraction * (endB - startB))));
+    /* Print any remaining command line arguments (not options). */
+    if (optind < argc) {
+        do {
+            const char* test = argv[optind++];
+            auto pos = testMap().find(test);
+            if (pos == testMap().end()) {
+                fprintf(stderr, "Unknown test '%s'\n", test);
+                exit(EXIT_FAILURE);
+            } else {
+                gRunTests.push_back(pos->second);
+            }
+        } while (optind < argc);
+    } else {
+        gRunTests.push_back(testMap()["shadowgrid"]);
     }
-private:
-    sp<RenderNode> createCard(int x, int y, int width, int height, SkColor color) {
-        sp<RenderNode> node = new RenderNode();
-        node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
-        node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
-
-        DisplayListCanvas* renderer = startRecording(node.get());
-        renderer->drawColor(color, SkXfermode::kSrcOver_Mode);
-        endRecording(renderer, node.get());
-        return node;
-    }
-};
-
-struct cstr_cmp {
-    bool operator()(const char *a, const char *b) const {
-        return std::strcmp(a, b) < 0;
-    }
-};
-
-typedef void (*testProc)(int);
-
-std::map<const char*, testProc, cstr_cmp> gTestMap {
-    {"shadowgrid", TreeContentAnimation::run<ShadowGridAnimation>},
-    {"shadowgrid2", TreeContentAnimation::run<ShadowGrid2Animation>},
-    {"rectgrid", TreeContentAnimation::run<RectGridAnimation> },
-    {"oval", TreeContentAnimation::run<OvalAnimation> },
-    {"partialinval", TreeContentAnimation::run<PartialInvalTest> },
-};
+}
 
 int main(int argc, char* argv[]) {
-    const char* testName = argc > 1 ? argv[1] : "shadowgrid";
-    testProc proc = gTestMap[testName];
-    if(!proc) {
-        printf("Error: couldn't find test %s\n", testName);
-        return 1;
-    }
-    int loopCount = 1;
-    if (argc > 2) {
-        loopCount = atoi(argv[2]);
-        if (!loopCount) {
-            printf("Invalid loop count!\n");
-            return 1;
+    parseOptions(argc, argv);
+
+    BenchmarkOptions opts;
+    opts.count = gFrameCount;
+    for (int i = 0; i < gRepeatCount; i++) {
+        for (auto&& test : gRunTests) {
+            test.functor(opts);
         }
     }
-    int frameCount = 150;
-    if (argc > 3) {
-        frameCount = atoi(argv[3]);
-        if (frameCount < 1) {
-            printf("Invalid frame count!\n");
-            return 1;
-        }
-    }
-    if (loopCount < 0) {
-        loopCount = INT_MAX;
-    }
-    for (int i = 0; i < loopCount; i++) {
-        proc(frameCount);
-    }
     printf("Success!\n");
     return 0;
 }
diff --git a/libs/hwui/utils/PaintUtils.h b/libs/hwui/utils/PaintUtils.h
index ba02f5f..d00236e 100644
--- a/libs/hwui/utils/PaintUtils.h
+++ b/libs/hwui/utils/PaintUtils.h
@@ -16,12 +16,19 @@
 #ifndef PAINT_UTILS_H
 #define PAINT_UTILS_H
 
+#include <utils/Blur.h>
+
 #include <SkColorFilter.h>
+#include <SkDrawLooper.h>
 #include <SkXfermode.h>
 
 namespace android {
 namespace uirenderer {
 
+/**
+ * Utility methods for accessing data within SkPaint, and providing defaults
+ * with optional SkPaint pointers.
+ */
 class PaintUtils {
 public:
 
@@ -73,6 +80,39 @@
         return (filter->getFlags() & SkColorFilter::kAlphaUnchanged_Flag) == 0;
     }
 
+    struct TextShadow {
+        SkScalar radius;
+        float dx;
+        float dy;
+        SkColor color;
+    };
+
+    static inline bool getTextShadow(const SkPaint* paint, TextShadow* textShadow) {
+        SkDrawLooper::BlurShadowRec blur;
+        if (paint && paint->getLooper() && paint->getLooper()->asABlurShadow(&blur)) {
+            if (textShadow) {
+                textShadow->radius = Blur::convertSigmaToRadius(blur.fSigma);
+                textShadow->dx = blur.fOffset.fX;
+                textShadow->dy = blur.fOffset.fY;
+                textShadow->color = blur.fColor;
+            }
+            return true;
+        }
+        return false;
+    }
+
+    static inline bool hasTextShadow(const SkPaint* paint) {
+        return getTextShadow(paint, nullptr);
+    }
+
+    static inline SkXfermode::Mode getXfermodeDirect(const SkPaint* paint) {
+        return paint ? getXfermode(paint->getXfermode()) : SkXfermode::kSrcOver_Mode;
+    }
+
+    static inline int getAlphaDirect(const SkPaint* paint) {
+        return paint ? paint->getAlpha() : 255;
+    }
+
 }; // class PaintUtils
 
 } /* namespace uirenderer */
diff --git a/libs/hwui/utils/TinyHashMap.h b/libs/hwui/utils/TinyHashMap.h
deleted file mode 100644
index 4ff9a42..0000000
--- a/libs/hwui/utils/TinyHashMap.h
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_HWUI_TINYHASHMAP_H
-#define ANDROID_HWUI_TINYHASHMAP_H
-
-#include <utils/BasicHashtable.h>
-
-namespace android {
-namespace uirenderer {
-
-/**
- * A very simple hash map that doesn't allow duplicate keys, overwriting the older entry.
- */
-template <typename TKey, typename TValue>
-class TinyHashMap {
-public:
-    typedef key_value_pair_t<TKey, TValue> TEntry;
-
-    /**
-     * Puts an entry in the hash, removing any existing entry with the same key
-     */
-    void put(TKey key, TValue value) {
-        hash_t hash = android::hash_type(key);
-
-        ssize_t index = mTable.find(-1, hash, key);
-        if (index != -1) {
-            mTable.removeAt(index);
-        }
-
-        TEntry initEntry(key, value);
-        mTable.add(hash, initEntry);
-    }
-
-    /**
-     * Return true if key is in the map, in which case stores the value in the output ref
-     */
-    bool get(TKey key, TValue& outValue) {
-        hash_t hash = android::hash_type(key);
-        ssize_t index = mTable.find(-1, hash, key);
-        if (index == -1) {
-            return false;
-        }
-        outValue = mTable.entryAt(index).value;
-        return true;
-    }
-
-    void clear() { mTable.clear(); }
-
-private:
-    BasicHashtable<TKey, TEntry> mTable;
-};
-
-}; // namespace uirenderer
-}; // namespace android
-
-#endif // ANDROID_HWUI_TINYHASHMAP_H
diff --git a/media/java/android/media/MediaActionSound.java b/media/java/android/media/MediaActionSound.java
index 2f4d136..1fee587 100644
--- a/media/java/android/media/MediaActionSound.java
+++ b/media/java/android/media/MediaActionSound.java
@@ -52,7 +52,7 @@
         "/system/media/audio/ui/camera_click.ogg",
         "/system/media/audio/ui/camera_focus.ogg",
         "/system/media/audio/ui/VideoRecord.ogg",
-        "/system/media/audio/ui/VideoRecord.ogg"
+        "/system/media/audio/ui/VideoStop.ogg"
     };
 
     private static final String TAG = "MediaActionSound";
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index bc1aeda..1c2c940 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -479,11 +479,6 @@
  *     <td>{} </p></td>
  *     <td>This method can be called in any state and calling it does not change
  *         the object state. </p></td></tr>
- * <tr><td>setPlaybackRate</p></td>
- *     <td>any </p></td>
- *     <td>{} </p></td>
- *     <td>This method can be called in any state and calling it does not change
- *         the object state. </p></td></tr>
  * <tr><td>setPlaybackParams</p></td>
  *     <td>any </p></td>
  *     <td>{} </p></td>
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index ed2c4cbd..8ac86b0 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -807,6 +807,32 @@
     public native void stop() throws IllegalStateException;
 
     /**
+     * Pauses recording. Call this after start(). You may resume recording
+     * with resume() without reconfiguration, as opposed to stop(). It does
+     * nothing if the recording is already paused.
+     *
+     * When the recording is paused and resumed, the resulting output would
+     * be as if nothing happend during paused period, immediately switching
+     * to the resumed scene.
+     *
+     * @throws IllegalStateException if it is called before start() or after
+     * stop()
+     * {@hide}
+     */
+    public native void pause() throws IllegalStateException;
+
+    /**
+     * Resumes recording. Call this after start(). It does nothing if the
+     * recording is not paused.
+     *
+     * @throws IllegalStateException if it is called before start() or after
+     * stop()
+     * @see android.media.MediaRecorder#pause
+     * {@hide}
+     */
+    public native void resume() throws IllegalStateException;
+
+    /**
      * Restarts the MediaRecorder to its idle state. After calling
      * this method, you will have to configure it again as if it had just been
      * constructed.
diff --git a/media/java/android/media/Metadata.java b/media/java/android/media/Metadata.java
index 54ad60e..4b8f81e 100644
--- a/media/java/android/media/Metadata.java
+++ b/media/java/android/media/Metadata.java
@@ -18,6 +18,7 @@
 
 import android.os.Parcel;
 import android.util.Log;
+import android.util.MathUtils;
 
 import java.util.Calendar;
 import java.util.Collections;
@@ -332,7 +333,14 @@
             }
 
             // Skip to the next one.
-            parcel.setDataPosition(start + size);
+            try {
+                parcel.setDataPosition(MathUtils.addOrThrow(start, size));
+            } catch (IllegalArgumentException e) {
+                Log.e(TAG, "Invalid size: " + e.getMessage());
+                error = true;
+                break;
+            }
+
             bytesLeft -= size;
             ++recCount;
         }
diff --git a/media/jni/android_media_MediaRecorder.cpp b/media/jni/android_media_MediaRecorder.cpp
index e05b348..701f7ac 100644
--- a/media/jni/android_media_MediaRecorder.cpp
+++ b/media/jni/android_media_MediaRecorder.cpp
@@ -399,6 +399,22 @@
 }
 
 static void
+android_media_MediaRecorder_pause(JNIEnv *env, jobject thiz)
+{
+    ALOGV("pause");
+    sp<MediaRecorder> mr = getMediaRecorder(env, thiz);
+    process_media_recorder_call(env, mr->pause(), "java/lang/RuntimeException", "pause failed.");
+}
+
+static void
+android_media_MediaRecorder_resume(JNIEnv *env, jobject thiz)
+{
+    ALOGV("resume");
+    sp<MediaRecorder> mr = getMediaRecorder(env, thiz);
+    process_media_recorder_call(env, mr->resume(), "java/lang/RuntimeException", "resume failed.");
+}
+
+static void
 android_media_MediaRecorder_native_reset(JNIEnv *env, jobject thiz)
 {
     ALOGV("native_reset");
@@ -528,6 +544,8 @@
     {"getMaxAmplitude",      "()I",                             (void *)android_media_MediaRecorder_native_getMaxAmplitude},
     {"start",                "()V",                             (void *)android_media_MediaRecorder_start},
     {"stop",                 "()V",                             (void *)android_media_MediaRecorder_stop},
+    {"pause",                "()V",                             (void *)android_media_MediaRecorder_pause},
+    {"resume",               "()V",                             (void *)android_media_MediaRecorder_resume},
     {"native_reset",         "()V",                             (void *)android_media_MediaRecorder_native_reset},
     {"release",              "()V",                             (void *)android_media_MediaRecorder_release},
     {"native_init",          "()V",                             (void *)android_media_MediaRecorder_native_init},
diff --git a/packages/DocumentsUI/AndroidManifest.xml b/packages/DocumentsUI/AndroidManifest.xml
index 295cb80..f9e8027 100644
--- a/packages/DocumentsUI/AndroidManifest.xml
+++ b/packages/DocumentsUI/AndroidManifest.xml
@@ -3,7 +3,7 @@
 
     <uses-permission android:name="android.permission.MANAGE_DOCUMENTS" />
     <uses-permission android:name="android.permission.REMOVE_TASKS" />
-    <uses-permission android:name="android.permission.REORDER_TASKS" />
+    <uses-permission android:name="android.permission.WAKE_LOCK" />
 
     <application
         android:name=".DocumentsApplication"
diff --git a/packages/DocumentsUI/res/color/item_doc_grid_overlay.xml b/packages/DocumentsUI/res/color/item_doc_grid_overlay.xml
index 6dcbb38..ab414a9 100644
--- a/packages/DocumentsUI/res/color/item_doc_grid_overlay.xml
+++ b/packages/DocumentsUI/res/color/item_doc_grid_overlay.xml
@@ -16,6 +16,10 @@
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
     <item
+        android:state_focused="true"
+        android:color="@color/platform_blue_a200"
+        android:alpha="0.1" />
+    <item
         android:state_activated="true"
         android:color="?android:attr/colorAccent"
         android:alpha="0.1" />
@@ -25,4 +29,4 @@
         android:alpha="0.5" />
     <item
         android:color="@android:color/transparent" />
-</selector>
\ No newline at end of file
+</selector>
diff --git a/packages/DocumentsUI/res/color/item_doc_list_background_activated.xml b/packages/DocumentsUI/res/color/item_doc_list_background_activated.xml
index 7d7a110..90e2b7e 100644
--- a/packages/DocumentsUI/res/color/item_doc_list_background_activated.xml
+++ b/packages/DocumentsUI/res/color/item_doc_list_background_activated.xml
@@ -16,6 +16,10 @@
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
     <item
+        android:state_focused="true"
+        android:color="@color/platform_blue_a200"
+        android:alpha="0.1" />
+    <item
         android:state_activated="true"
         android:color="?android:attr/colorAccent"
         android:alpha="0.1" />
diff --git a/packages/DocumentsUI/res/menu/activity.xml b/packages/DocumentsUI/res/menu/activity.xml
index 673a254..7e0649b 100644
--- a/packages/DocumentsUI/res/menu/activity.xml
+++ b/packages/DocumentsUI/res/menu/activity.xml
@@ -23,13 +23,6 @@
         android:actionViewClass="android.widget.SearchView"
         android:imeOptions="actionSearch" />
     <item
-        android:id="@+id/menu_create_dir"
-        android:title="@string/menu_create_dir"
-        android:icon="@drawable/ic_menu_new_folder"
-        android:alphabeticShortcut="e"
-        android:showAsAction="always"
-        android:visible="false" />
-    <item
         android:id="@+id/menu_sort"
         android:title="@string/menu_sort"
         android:icon="@drawable/ic_menu_sortby"
@@ -63,6 +56,13 @@
         android:showAsAction="never"
         android:visible="false" />
     <item
+        android:id="@+id/menu_create_dir"
+        android:title="@string/menu_create_dir"
+        android:icon="@drawable/ic_menu_new_folder"
+        android:alphabeticShortcut="e"
+        android:showAsAction="always"
+        android:visible="false" />
+    <item
         android:id="@+id/menu_paste_from_clipboard"
         android:title="@string/menu_paste_from_clipboard"
         android:alphabeticShortcut="v"
@@ -70,11 +70,11 @@
         android:visible="false" />
     <!-- Copy action is defined in mode_directory.xml -->
     <item
-        android:id="@+id/menu_advanced"
+        android:id="@+id/menu_file_size"
         android:showAsAction="never"
         android:visible="false" />
     <item
-        android:id="@+id/menu_file_size"
+        android:id="@+id/menu_advanced"
         android:showAsAction="never"
         android:visible="false" />
     <item
diff --git a/packages/DocumentsUI/res/values-af/strings.xml b/packages/DocumentsUI/res/values-af/strings.xml
index c2b6dbc..3daecc7 100644
--- a/packages/DocumentsUI/res/values-af/strings.xml
+++ b/packages/DocumentsUI/res/values-af/strings.xml
@@ -20,7 +20,7 @@
     <string name="files_label" msgid="6051402950202690279">"Lêers"</string>
     <string name="title_open" msgid="4353228937663917801">"Maak oop vanuit"</string>
     <string name="title_save" msgid="2433679664882857999">"Stoor na"</string>
-    <string name="menu_create_dir" msgid="5947289605844398389">"Skep vouer"</string>
+    <string name="menu_create_dir" msgid="2547620241173881754">"Nuwe vouer"</string>
     <string name="menu_grid" msgid="6878021334497835259">"Roosteraansig"</string>
     <string name="menu_list" msgid="7279285939892417279">"Lysaansig"</string>
     <string name="menu_sort" msgid="7677740407158414452">"Sorteer volgens"</string>
@@ -33,6 +33,7 @@
     <string name="menu_select_all" msgid="8323579667348729928">"Kies alles"</string>
     <string name="menu_copy" msgid="3612326052677229148">"Kopieer na …"</string>
     <string name="menu_move" msgid="1828090633118079817">"Skuif na …"</string>
+    <string name="menu_new_window" msgid="1226032889278727538">"Nuwe venster"</string>
     <string name="menu_copy_to_clipboard" msgid="489311381979634291">"Kopieer"</string>
     <string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Plak"</string>
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Wys interne berging"</string>
diff --git a/packages/DocumentsUI/res/values-am/strings.xml b/packages/DocumentsUI/res/values-am/strings.xml
index b82fa93..43db786 100644
--- a/packages/DocumentsUI/res/values-am/strings.xml
+++ b/packages/DocumentsUI/res/values-am/strings.xml
@@ -20,7 +20,7 @@
     <string name="files_label" msgid="6051402950202690279">"ፋይሎች"</string>
     <string name="title_open" msgid="4353228937663917801">"ክፈት ከ"</string>
     <string name="title_save" msgid="2433679664882857999">"አስቀምጥ ወደ"</string>
-    <string name="menu_create_dir" msgid="5947289605844398389">"አቃፊ ፍጠር"</string>
+    <string name="menu_create_dir" msgid="2547620241173881754">"አዲስ አቃፊ"</string>
     <string name="menu_grid" msgid="6878021334497835259">"የፍርግርግ እይታ"</string>
     <string name="menu_list" msgid="7279285939892417279">"የዝርዝር እይታ"</string>
     <string name="menu_sort" msgid="7677740407158414452">"ደርድር በ"</string>
@@ -33,6 +33,7 @@
     <string name="menu_select_all" msgid="8323579667348729928">"ሁሉንም ምረጥ"</string>
     <string name="menu_copy" msgid="3612326052677229148">"ቅዳ ወደ…"</string>
     <string name="menu_move" msgid="1828090633118079817">"ይውሰዱ ወደ..."</string>
+    <string name="menu_new_window" msgid="1226032889278727538">"አዲሰ መስኮት"</string>
     <string name="menu_copy_to_clipboard" msgid="489311381979634291">"ቅዳ"</string>
     <string name="menu_paste_from_clipboard" msgid="2071583031180257091">"ለጥፍ"</string>
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"ውስጣዊ ማከማቻ አሳይ"</string>
diff --git a/packages/DocumentsUI/res/values-ar/strings.xml b/packages/DocumentsUI/res/values-ar/strings.xml
index c7c9ef2..88c42d03 100644
--- a/packages/DocumentsUI/res/values-ar/strings.xml
+++ b/packages/DocumentsUI/res/values-ar/strings.xml
@@ -20,7 +20,7 @@
     <string name="files_label" msgid="6051402950202690279">"الملفات"</string>
     <string name="title_open" msgid="4353228937663917801">"فتح من"</string>
     <string name="title_save" msgid="2433679664882857999">"حفظ في"</string>
-    <string name="menu_create_dir" msgid="5947289605844398389">"إنشاء مجلد"</string>
+    <string name="menu_create_dir" msgid="2547620241173881754">"مجلد جديد"</string>
     <string name="menu_grid" msgid="6878021334497835259">"عرض الشبكة"</string>
     <string name="menu_list" msgid="7279285939892417279">"عرض القائمة"</string>
     <string name="menu_sort" msgid="7677740407158414452">"ترتيب بحسب"</string>
@@ -33,6 +33,7 @@
     <string name="menu_select_all" msgid="8323579667348729928">"تحديد الكل"</string>
     <string name="menu_copy" msgid="3612326052677229148">"نسخ إلى…"</string>
     <string name="menu_move" msgid="1828090633118079817">"نقل إلى..."</string>
+    <string name="menu_new_window" msgid="1226032889278727538">"نافذة جديدة"</string>
     <string name="menu_copy_to_clipboard" msgid="489311381979634291">"نسخ"</string>
     <string name="menu_paste_from_clipboard" msgid="2071583031180257091">"لصق"</string>
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"إظهار وحدة التخزين الداخلية"</string>
@@ -45,8 +46,7 @@
     <string name="button_copy" msgid="8706475544635021302">"نسخ"</string>
     <string name="button_move" msgid="2202666023104202232">"نقل"</string>
     <string name="button_dismiss" msgid="3714065566893946085">"إزالة"</string>
-    <!-- no translation found for button_retry (4392027584153752797) -->
-    <skip />
+    <string name="button_retry" msgid="4392027584153752797">"إعادة المحاولة"</string>
     <string name="sort_name" msgid="9183560467917256779">"بحسب الاسم"</string>
     <string name="sort_date" msgid="586080032956151448">"بحسب تاريخ التعديل"</string>
     <string name="sort_size" msgid="3350681319735474741">"بحسب الحجم"</string>
diff --git a/packages/DocumentsUI/res/values-az-rAZ/strings.xml b/packages/DocumentsUI/res/values-az-rAZ/strings.xml
index 8c79ceb..f91f2ee 100644
--- a/packages/DocumentsUI/res/values-az-rAZ/strings.xml
+++ b/packages/DocumentsUI/res/values-az-rAZ/strings.xml
@@ -20,7 +20,7 @@
     <string name="files_label" msgid="6051402950202690279">"Fayllar"</string>
     <string name="title_open" msgid="4353228937663917801">"Vasitəsilə açın"</string>
     <string name="title_save" msgid="2433679664882857999">"buraya saxlayın"</string>
-    <string name="menu_create_dir" msgid="5947289605844398389">"Qovluq yaradın"</string>
+    <string name="menu_create_dir" msgid="2547620241173881754">"Yeni qovluq"</string>
     <string name="menu_grid" msgid="6878021334497835259">"Torlu görünüş"</string>
     <string name="menu_list" msgid="7279285939892417279">"Siyahı görünüşü"</string>
     <string name="menu_sort" msgid="7677740407158414452">"Bunlardan biri üzrə sırala"</string>
@@ -33,6 +33,7 @@
     <string name="menu_select_all" msgid="8323579667348729928">"Hamısını seçin"</string>
     <string name="menu_copy" msgid="3612326052677229148">"Buraya kopyalayın:"</string>
     <string name="menu_move" msgid="1828090633118079817">"Köçürün…"</string>
+    <string name="menu_new_window" msgid="1226032889278727538">"Yeni pəncərə"</string>
     <string name="menu_copy_to_clipboard" msgid="489311381979634291">"Kopyalayın"</string>
     <string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Yerləşdirin"</string>
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Daxili yaddaşı göstərin"</string>
@@ -45,8 +46,7 @@
     <string name="button_copy" msgid="8706475544635021302">"Kopyala"</string>
     <string name="button_move" msgid="2202666023104202232">"Köçürün"</string>
     <string name="button_dismiss" msgid="3714065566893946085">"Rədd edin"</string>
-    <!-- no translation found for button_retry (4392027584153752797) -->
-    <skip />
+    <string name="button_retry" msgid="4392027584153752797">"Yenidən cəhd edin"</string>
     <string name="sort_name" msgid="9183560467917256779">"Ad üzrə"</string>
     <string name="sort_date" msgid="586080032956151448">"Tarix üzrə dəyişmiş"</string>
     <string name="sort_size" msgid="3350681319735474741">"Ölçü üzrə"</string>
diff --git a/packages/DocumentsUI/res/values-bg/strings.xml b/packages/DocumentsUI/res/values-bg/strings.xml
index fdf57d0..67a1c6a 100644
--- a/packages/DocumentsUI/res/values-bg/strings.xml
+++ b/packages/DocumentsUI/res/values-bg/strings.xml
@@ -20,7 +20,7 @@
     <string name="files_label" msgid="6051402950202690279">"Файлове"</string>
     <string name="title_open" msgid="4353228937663917801">"Отваряне от"</string>
     <string name="title_save" msgid="2433679664882857999">"Запазване във:"</string>
-    <string name="menu_create_dir" msgid="5947289605844398389">"Създаване на папка"</string>
+    <string name="menu_create_dir" msgid="2547620241173881754">"Нова папка"</string>
     <string name="menu_grid" msgid="6878021334497835259">"Изглед в мрежа"</string>
     <string name="menu_list" msgid="7279285939892417279">"Списъчен изглед"</string>
     <string name="menu_sort" msgid="7677740407158414452">"Сортиране по"</string>
@@ -33,6 +33,7 @@
     <string name="menu_select_all" msgid="8323579667348729928">"Избиране на всичко"</string>
     <string name="menu_copy" msgid="3612326052677229148">"Копиране във…"</string>
     <string name="menu_move" msgid="1828090633118079817">"Преместване във…"</string>
+    <string name="menu_new_window" msgid="1226032889278727538">"Нов прозорец"</string>
     <string name="menu_copy_to_clipboard" msgid="489311381979634291">"Копиране"</string>
     <string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Поставяне"</string>
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Вътр. хранилище: Показв."</string>
@@ -45,8 +46,7 @@
     <string name="button_copy" msgid="8706475544635021302">"Копиране"</string>
     <string name="button_move" msgid="2202666023104202232">"Преместване"</string>
     <string name="button_dismiss" msgid="3714065566893946085">"Отхвърляне"</string>
-    <!-- no translation found for button_retry (4392027584153752797) -->
-    <skip />
+    <string name="button_retry" msgid="4392027584153752797">"Нов опит"</string>
     <string name="sort_name" msgid="9183560467917256779">"По име"</string>
     <string name="sort_date" msgid="586080032956151448">"По дата на промяната"</string>
     <string name="sort_size" msgid="3350681319735474741">"По размер"</string>
diff --git a/packages/DocumentsUI/res/values-bn-rBD/strings.xml b/packages/DocumentsUI/res/values-bn-rBD/strings.xml
index 7caf57f..b01cc84e 100644
--- a/packages/DocumentsUI/res/values-bn-rBD/strings.xml
+++ b/packages/DocumentsUI/res/values-bn-rBD/strings.xml
@@ -20,7 +20,7 @@
     <string name="files_label" msgid="6051402950202690279">"ফাইলগুলি"</string>
     <string name="title_open" msgid="4353228937663917801">"এখান থেকে খুলুন"</string>
     <string name="title_save" msgid="2433679664882857999">"এতে সংরক্ষণ করুন"</string>
-    <string name="menu_create_dir" msgid="5947289605844398389">"ফোল্ডার তৈরি করুন"</string>
+    <string name="menu_create_dir" msgid="2547620241173881754">"নতুন ফোল্ডার"</string>
     <string name="menu_grid" msgid="6878021334497835259">"গ্রিড দৃশ্য"</string>
     <string name="menu_list" msgid="7279285939892417279">"তালিকা দৃশ্য"</string>
     <string name="menu_sort" msgid="7677740407158414452">"এই অনুসারে বাছুন"</string>
@@ -33,6 +33,7 @@
     <string name="menu_select_all" msgid="8323579667348729928">"সবগুলি নির্বাচন করুন"</string>
     <string name="menu_copy" msgid="3612326052677229148">"এতে অনুলিপি করুন…"</string>
     <string name="menu_move" msgid="1828090633118079817">"এতে সরান..."</string>
+    <string name="menu_new_window" msgid="1226032889278727538">"নতুন উইন্ডো"</string>
     <string name="menu_copy_to_clipboard" msgid="489311381979634291">"প্রতিলিপি করুন"</string>
     <string name="menu_paste_from_clipboard" msgid="2071583031180257091">"আটকান"</string>
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"অভ্যন্তরীণ সঞ্চয়স্থান দেখান"</string>
diff --git a/packages/DocumentsUI/res/values-ca/strings.xml b/packages/DocumentsUI/res/values-ca/strings.xml
index ab365ce..663608b 100644
--- a/packages/DocumentsUI/res/values-ca/strings.xml
+++ b/packages/DocumentsUI/res/values-ca/strings.xml
@@ -20,7 +20,7 @@
     <string name="files_label" msgid="6051402950202690279">"Fitxers"</string>
     <string name="title_open" msgid="4353228937663917801">"Obre des de"</string>
     <string name="title_save" msgid="2433679664882857999">"Desa a"</string>
-    <string name="menu_create_dir" msgid="5947289605844398389">"Crea una carpeta"</string>
+    <string name="menu_create_dir" msgid="2547620241173881754">"Carpeta nova"</string>
     <string name="menu_grid" msgid="6878021334497835259">"Visualització de quadrícula"</string>
     <string name="menu_list" msgid="7279285939892417279">"Visualització de llista"</string>
     <string name="menu_sort" msgid="7677740407158414452">"Ordena per"</string>
@@ -33,6 +33,7 @@
     <string name="menu_select_all" msgid="8323579667348729928">"Selecciona-ho tot"</string>
     <string name="menu_copy" msgid="3612326052677229148">"Copia a…"</string>
     <string name="menu_move" msgid="1828090633118079817">"Mou a..."</string>
+    <string name="menu_new_window" msgid="1226032889278727538">"Finestra nova"</string>
     <string name="menu_copy_to_clipboard" msgid="489311381979634291">"Copia"</string>
     <string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Enganxa"</string>
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Mostra emmagatz. intern"</string>
@@ -45,8 +46,7 @@
     <string name="button_copy" msgid="8706475544635021302">"Copia"</string>
     <string name="button_move" msgid="2202666023104202232">"Desplaça"</string>
     <string name="button_dismiss" msgid="3714065566893946085">"Ignora"</string>
-    <!-- no translation found for button_retry (4392027584153752797) -->
-    <skip />
+    <string name="button_retry" msgid="4392027584153752797">"Torna-ho a provar"</string>
     <string name="sort_name" msgid="9183560467917256779">"Per nom"</string>
     <string name="sort_date" msgid="586080032956151448">"Per data de modificació"</string>
     <string name="sort_size" msgid="3350681319735474741">"Per mida"</string>
diff --git a/packages/DocumentsUI/res/values-cs/strings.xml b/packages/DocumentsUI/res/values-cs/strings.xml
index 62b313c..81d8dc8 100644
--- a/packages/DocumentsUI/res/values-cs/strings.xml
+++ b/packages/DocumentsUI/res/values-cs/strings.xml
@@ -20,7 +20,7 @@
     <string name="files_label" msgid="6051402950202690279">"Soubory"</string>
     <string name="title_open" msgid="4353228937663917801">"Otevřít"</string>
     <string name="title_save" msgid="2433679664882857999">"Uložit do"</string>
-    <string name="menu_create_dir" msgid="5947289605844398389">"Vytvořit složku"</string>
+    <string name="menu_create_dir" msgid="2547620241173881754">"Nová složka"</string>
     <string name="menu_grid" msgid="6878021334497835259">"Mřížkové zobrazení"</string>
     <string name="menu_list" msgid="7279285939892417279">"Zobrazení seznamu"</string>
     <string name="menu_sort" msgid="7677740407158414452">"Řadit podle"</string>
@@ -33,6 +33,7 @@
     <string name="menu_select_all" msgid="8323579667348729928">"Vybrat vše"</string>
     <string name="menu_copy" msgid="3612326052677229148">"Kopírovat do…"</string>
     <string name="menu_move" msgid="1828090633118079817">"Přesunout do…"</string>
+    <string name="menu_new_window" msgid="1226032889278727538">"Nové okno"</string>
     <string name="menu_copy_to_clipboard" msgid="489311381979634291">"Kopírovat"</string>
     <string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Vložit"</string>
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Zobrazit inter. úložiště"</string>
diff --git a/packages/DocumentsUI/res/values-da/strings.xml b/packages/DocumentsUI/res/values-da/strings.xml
index c3d7cdd..e109048 100644
--- a/packages/DocumentsUI/res/values-da/strings.xml
+++ b/packages/DocumentsUI/res/values-da/strings.xml
@@ -20,7 +20,7 @@
     <string name="files_label" msgid="6051402950202690279">"Filer"</string>
     <string name="title_open" msgid="4353228937663917801">"Åbn fra"</string>
     <string name="title_save" msgid="2433679664882857999">"Gem i"</string>
-    <string name="menu_create_dir" msgid="5947289605844398389">"Opret mappe"</string>
+    <string name="menu_create_dir" msgid="2547620241173881754">"Ny mappe"</string>
     <string name="menu_grid" msgid="6878021334497835259">"Gittervisning"</string>
     <string name="menu_list" msgid="7279285939892417279">"Listevisning"</string>
     <string name="menu_sort" msgid="7677740407158414452">"Sortér efter"</string>
@@ -33,6 +33,7 @@
     <string name="menu_select_all" msgid="8323579667348729928">"Markér alle"</string>
     <string name="menu_copy" msgid="3612326052677229148">"Kopiér til…"</string>
     <string name="menu_move" msgid="1828090633118079817">"Flyt til…"</string>
+    <string name="menu_new_window" msgid="1226032889278727538">"Nyt vindue"</string>
     <string name="menu_copy_to_clipboard" msgid="489311381979634291">"Kopiér"</string>
     <string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Indsæt"</string>
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Vis intern lagerplads"</string>
diff --git a/packages/DocumentsUI/res/values-de/strings.xml b/packages/DocumentsUI/res/values-de/strings.xml
index f88b5c4..7c467ef 100644
--- a/packages/DocumentsUI/res/values-de/strings.xml
+++ b/packages/DocumentsUI/res/values-de/strings.xml
@@ -20,7 +20,7 @@
     <string name="files_label" msgid="6051402950202690279">"Dateien"</string>
     <string name="title_open" msgid="4353228937663917801">"Öffnen von"</string>
     <string name="title_save" msgid="2433679664882857999">"Speichern unter"</string>
-    <string name="menu_create_dir" msgid="5947289605844398389">"Ordner erstellen"</string>
+    <string name="menu_create_dir" msgid="2547620241173881754">"Neuer Ordner"</string>
     <string name="menu_grid" msgid="6878021334497835259">"Rasteransicht"</string>
     <string name="menu_list" msgid="7279285939892417279">"Listenansicht"</string>
     <string name="menu_sort" msgid="7677740407158414452">"Sortieren nach"</string>
@@ -33,6 +33,7 @@
     <string name="menu_select_all" msgid="8323579667348729928">"Alle auswählen"</string>
     <string name="menu_copy" msgid="3612326052677229148">"Kopieren nach..."</string>
     <string name="menu_move" msgid="1828090633118079817">"Verschieben nach…"</string>
+    <string name="menu_new_window" msgid="1226032889278727538">"Neues Fenster"</string>
     <string name="menu_copy_to_clipboard" msgid="489311381979634291">"Kopieren"</string>
     <string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Einfügen"</string>
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Int. Speicher anzeigen"</string>
diff --git a/packages/DocumentsUI/res/values-el/strings.xml b/packages/DocumentsUI/res/values-el/strings.xml
index dcca46c..a350002 100644
--- a/packages/DocumentsUI/res/values-el/strings.xml
+++ b/packages/DocumentsUI/res/values-el/strings.xml
@@ -20,7 +20,7 @@
     <string name="files_label" msgid="6051402950202690279">"Αρχεία"</string>
     <string name="title_open" msgid="4353228937663917801">"Άνοιγμα από"</string>
     <string name="title_save" msgid="2433679664882857999">"Αποθήκευση σε"</string>
-    <string name="menu_create_dir" msgid="5947289605844398389">"Δημιουργία φακέλου"</string>
+    <string name="menu_create_dir" msgid="2547620241173881754">"Νέος φάκελος"</string>
     <string name="menu_grid" msgid="6878021334497835259">"Προβολή πλέγματος"</string>
     <string name="menu_list" msgid="7279285939892417279">"Προβολή λίστας"</string>
     <string name="menu_sort" msgid="7677740407158414452">"Ταξινόμηση κατά"</string>
@@ -33,6 +33,7 @@
     <string name="menu_select_all" msgid="8323579667348729928">"Επιλογή όλων"</string>
     <string name="menu_copy" msgid="3612326052677229148">"Αντιγραφή σε…"</string>
     <string name="menu_move" msgid="1828090633118079817">"Μετακίνηση σε..."</string>
+    <string name="menu_new_window" msgid="1226032889278727538">"Νέο παράθυρο"</string>
     <string name="menu_copy_to_clipboard" msgid="489311381979634291">"Αντιγραφή"</string>
     <string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Επικόλληση"</string>
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Εμφ.εσωτ.χώρου αποθήκ."</string>
diff --git a/packages/DocumentsUI/res/values-en-rAU/strings.xml b/packages/DocumentsUI/res/values-en-rAU/strings.xml
index 26fd30f..e9ba34b 100644
--- a/packages/DocumentsUI/res/values-en-rAU/strings.xml
+++ b/packages/DocumentsUI/res/values-en-rAU/strings.xml
@@ -20,7 +20,7 @@
     <string name="files_label" msgid="6051402950202690279">"Files"</string>
     <string name="title_open" msgid="4353228937663917801">"Open from"</string>
     <string name="title_save" msgid="2433679664882857999">"Save to"</string>
-    <string name="menu_create_dir" msgid="5947289605844398389">"Create folder"</string>
+    <string name="menu_create_dir" msgid="2547620241173881754">"New folder"</string>
     <string name="menu_grid" msgid="6878021334497835259">"Grid view"</string>
     <string name="menu_list" msgid="7279285939892417279">"List view"</string>
     <string name="menu_sort" msgid="7677740407158414452">"Sort by"</string>
@@ -33,6 +33,7 @@
     <string name="menu_select_all" msgid="8323579667348729928">"Select all"</string>
     <string name="menu_copy" msgid="3612326052677229148">"Copy to…"</string>
     <string name="menu_move" msgid="1828090633118079817">"Move to…"</string>
+    <string name="menu_new_window" msgid="1226032889278727538">"New window"</string>
     <string name="menu_copy_to_clipboard" msgid="489311381979634291">"Copy"</string>
     <string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Paste"</string>
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Show internal storage"</string>
diff --git a/packages/DocumentsUI/res/values-en-rGB/strings.xml b/packages/DocumentsUI/res/values-en-rGB/strings.xml
index 26fd30f..e9ba34b 100644
--- a/packages/DocumentsUI/res/values-en-rGB/strings.xml
+++ b/packages/DocumentsUI/res/values-en-rGB/strings.xml
@@ -20,7 +20,7 @@
     <string name="files_label" msgid="6051402950202690279">"Files"</string>
     <string name="title_open" msgid="4353228937663917801">"Open from"</string>
     <string name="title_save" msgid="2433679664882857999">"Save to"</string>
-    <string name="menu_create_dir" msgid="5947289605844398389">"Create folder"</string>
+    <string name="menu_create_dir" msgid="2547620241173881754">"New folder"</string>
     <string name="menu_grid" msgid="6878021334497835259">"Grid view"</string>
     <string name="menu_list" msgid="7279285939892417279">"List view"</string>
     <string name="menu_sort" msgid="7677740407158414452">"Sort by"</string>
@@ -33,6 +33,7 @@
     <string name="menu_select_all" msgid="8323579667348729928">"Select all"</string>
     <string name="menu_copy" msgid="3612326052677229148">"Copy to…"</string>
     <string name="menu_move" msgid="1828090633118079817">"Move to…"</string>
+    <string name="menu_new_window" msgid="1226032889278727538">"New window"</string>
     <string name="menu_copy_to_clipboard" msgid="489311381979634291">"Copy"</string>
     <string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Paste"</string>
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Show internal storage"</string>
diff --git a/packages/DocumentsUI/res/values-en-rIN/strings.xml b/packages/DocumentsUI/res/values-en-rIN/strings.xml
index 26fd30f..e9ba34b 100644
--- a/packages/DocumentsUI/res/values-en-rIN/strings.xml
+++ b/packages/DocumentsUI/res/values-en-rIN/strings.xml
@@ -20,7 +20,7 @@
     <string name="files_label" msgid="6051402950202690279">"Files"</string>
     <string name="title_open" msgid="4353228937663917801">"Open from"</string>
     <string name="title_save" msgid="2433679664882857999">"Save to"</string>
-    <string name="menu_create_dir" msgid="5947289605844398389">"Create folder"</string>
+    <string name="menu_create_dir" msgid="2547620241173881754">"New folder"</string>
     <string name="menu_grid" msgid="6878021334497835259">"Grid view"</string>
     <string name="menu_list" msgid="7279285939892417279">"List view"</string>
     <string name="menu_sort" msgid="7677740407158414452">"Sort by"</string>
@@ -33,6 +33,7 @@
     <string name="menu_select_all" msgid="8323579667348729928">"Select all"</string>
     <string name="menu_copy" msgid="3612326052677229148">"Copy to…"</string>
     <string name="menu_move" msgid="1828090633118079817">"Move to…"</string>
+    <string name="menu_new_window" msgid="1226032889278727538">"New window"</string>
     <string name="menu_copy_to_clipboard" msgid="489311381979634291">"Copy"</string>
     <string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Paste"</string>
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Show internal storage"</string>
diff --git a/packages/DocumentsUI/res/values-es-rUS/strings.xml b/packages/DocumentsUI/res/values-es-rUS/strings.xml
index 6e805b9..8e0a18cc 100644
--- a/packages/DocumentsUI/res/values-es-rUS/strings.xml
+++ b/packages/DocumentsUI/res/values-es-rUS/strings.xml
@@ -20,7 +20,7 @@
     <string name="files_label" msgid="6051402950202690279">"Archivos"</string>
     <string name="title_open" msgid="4353228937663917801">"Abrir desde"</string>
     <string name="title_save" msgid="2433679664882857999">"Guardar en"</string>
-    <string name="menu_create_dir" msgid="5947289605844398389">"Crear carpeta"</string>
+    <string name="menu_create_dir" msgid="2547620241173881754">"Carpeta nueva"</string>
     <string name="menu_grid" msgid="6878021334497835259">"Vista de cuadrícula"</string>
     <string name="menu_list" msgid="7279285939892417279">"Vista de lista"</string>
     <string name="menu_sort" msgid="7677740407158414452">"Ordenar por"</string>
@@ -33,6 +33,7 @@
     <string name="menu_select_all" msgid="8323579667348729928">"Seleccionar todo"</string>
     <string name="menu_copy" msgid="3612326052677229148">"Copiar a…"</string>
     <string name="menu_move" msgid="1828090633118079817">"Mover a…"</string>
+    <string name="menu_new_window" msgid="1226032889278727538">"Ventana nueva"</string>
     <string name="menu_copy_to_clipboard" msgid="489311381979634291">"Copiar"</string>
     <string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Pegar"</string>
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Mostrar almacen. interno"</string>
diff --git a/packages/DocumentsUI/res/values-es/strings.xml b/packages/DocumentsUI/res/values-es/strings.xml
index 8fa1d2e..cf0b607 100644
--- a/packages/DocumentsUI/res/values-es/strings.xml
+++ b/packages/DocumentsUI/res/values-es/strings.xml
@@ -20,7 +20,7 @@
     <string name="files_label" msgid="6051402950202690279">"Archivos"</string>
     <string name="title_open" msgid="4353228937663917801">"Abrir desde"</string>
     <string name="title_save" msgid="2433679664882857999">"Guardar en"</string>
-    <string name="menu_create_dir" msgid="5947289605844398389">"Crear carpeta"</string>
+    <string name="menu_create_dir" msgid="2547620241173881754">"Nueva carpeta"</string>
     <string name="menu_grid" msgid="6878021334497835259">"Vista de cuadrícula"</string>
     <string name="menu_list" msgid="7279285939892417279">"Vista de lista"</string>
     <string name="menu_sort" msgid="7677740407158414452">"Ordenar por"</string>
@@ -33,6 +33,7 @@
     <string name="menu_select_all" msgid="8323579667348729928">"Seleccionar todo"</string>
     <string name="menu_copy" msgid="3612326052677229148">"Copiar en…"</string>
     <string name="menu_move" msgid="1828090633118079817">"Mover a…"</string>
+    <string name="menu_new_window" msgid="1226032889278727538">"Nueva ventana"</string>
     <string name="menu_copy_to_clipboard" msgid="489311381979634291">"Copiar"</string>
     <string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Pegar"</string>
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Mostrar almac. interno"</string>
diff --git a/packages/DocumentsUI/res/values-et-rEE/strings.xml b/packages/DocumentsUI/res/values-et-rEE/strings.xml
index 7008885..76d1f48 100644
--- a/packages/DocumentsUI/res/values-et-rEE/strings.xml
+++ b/packages/DocumentsUI/res/values-et-rEE/strings.xml
@@ -20,7 +20,7 @@
     <string name="files_label" msgid="6051402950202690279">"Failid"</string>
     <string name="title_open" msgid="4353228937663917801">"Ava:"</string>
     <string name="title_save" msgid="2433679664882857999">"Salvesta:"</string>
-    <string name="menu_create_dir" msgid="5947289605844398389">"Loo kaust"</string>
+    <string name="menu_create_dir" msgid="2547620241173881754">"Uus kaust"</string>
     <string name="menu_grid" msgid="6878021334497835259">"Ruudustikkuva"</string>
     <string name="menu_list" msgid="7279285939892417279">"Loendikuva"</string>
     <string name="menu_sort" msgid="7677740407158414452">"Sortimisalus:"</string>
@@ -33,6 +33,7 @@
     <string name="menu_select_all" msgid="8323579667348729928">"Vali kõik"</string>
     <string name="menu_copy" msgid="3612326052677229148">"Kopeeri asukohta ..."</string>
     <string name="menu_move" msgid="1828090633118079817">"Teisaldamine kohta ..."</string>
+    <string name="menu_new_window" msgid="1226032889278727538">"Uus aken"</string>
     <string name="menu_copy_to_clipboard" msgid="489311381979634291">"Kopeeri"</string>
     <string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Kleebi"</string>
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Kuva sis. salvestusruum"</string>
@@ -45,8 +46,7 @@
     <string name="button_copy" msgid="8706475544635021302">"Kopeeri"</string>
     <string name="button_move" msgid="2202666023104202232">"Teisalda"</string>
     <string name="button_dismiss" msgid="3714065566893946085">"Loobu"</string>
-    <!-- no translation found for button_retry (4392027584153752797) -->
-    <skip />
+    <string name="button_retry" msgid="4392027584153752797">"Proovi uuesti"</string>
     <string name="sort_name" msgid="9183560467917256779">"Nime järgi"</string>
     <string name="sort_date" msgid="586080032956151448">"Muutmiskuupäeva järgi"</string>
     <string name="sort_size" msgid="3350681319735474741">"Suuruse järgi"</string>
diff --git a/packages/DocumentsUI/res/values-eu-rES/strings.xml b/packages/DocumentsUI/res/values-eu-rES/strings.xml
index 17d7c66..f24cf60 100644
--- a/packages/DocumentsUI/res/values-eu-rES/strings.xml
+++ b/packages/DocumentsUI/res/values-eu-rES/strings.xml
@@ -20,7 +20,7 @@
     <string name="files_label" msgid="6051402950202690279">"Fitxategiak"</string>
     <string name="title_open" msgid="4353228937663917801">"Ireki hemendik"</string>
     <string name="title_save" msgid="2433679664882857999">"Gorde hemen"</string>
-    <string name="menu_create_dir" msgid="5947289605844398389">"Sortu karpeta"</string>
+    <string name="menu_create_dir" msgid="2547620241173881754">"Karpeta berria"</string>
     <string name="menu_grid" msgid="6878021334497835259">"Sareta-ikuspegia"</string>
     <string name="menu_list" msgid="7279285939892417279">"Zerrenda-ikuspegia"</string>
     <string name="menu_sort" msgid="7677740407158414452">"Ordenatzeko irizpidea"</string>
@@ -33,6 +33,7 @@
     <string name="menu_select_all" msgid="8323579667348729928">"Hautatu guztiak"</string>
     <string name="menu_copy" msgid="3612326052677229148">"Kopiatu hemen…"</string>
     <string name="menu_move" msgid="1828090633118079817">"Eraman hona…"</string>
+    <string name="menu_new_window" msgid="1226032889278727538">"Leiho berria"</string>
     <string name="menu_copy_to_clipboard" msgid="489311381979634291">"Kopiatu"</string>
     <string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Itsatsi"</string>
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Erakutsi barneko memoria"</string>
@@ -45,8 +46,7 @@
     <string name="button_copy" msgid="8706475544635021302">"Kopiatu"</string>
     <string name="button_move" msgid="2202666023104202232">"Mugitu"</string>
     <string name="button_dismiss" msgid="3714065566893946085">"Baztertu"</string>
-    <!-- no translation found for button_retry (4392027584153752797) -->
-    <skip />
+    <string name="button_retry" msgid="4392027584153752797">"Saiatu berriro"</string>
     <string name="sort_name" msgid="9183560467917256779">"Izenaren arabera"</string>
     <string name="sort_date" msgid="586080032956151448">"Aldatze-dataren arabera"</string>
     <string name="sort_size" msgid="3350681319735474741">"Tamainaren arabera"</string>
diff --git a/packages/DocumentsUI/res/values-fa/strings.xml b/packages/DocumentsUI/res/values-fa/strings.xml
index c4f6d58..b504db5 100644
--- a/packages/DocumentsUI/res/values-fa/strings.xml
+++ b/packages/DocumentsUI/res/values-fa/strings.xml
@@ -20,7 +20,7 @@
     <string name="files_label" msgid="6051402950202690279">"فایل‌ها"</string>
     <string name="title_open" msgid="4353228937663917801">"باز کردن از"</string>
     <string name="title_save" msgid="2433679664882857999">"ذخیره در"</string>
-    <string name="menu_create_dir" msgid="5947289605844398389">"ایجاد پوشه"</string>
+    <string name="menu_create_dir" msgid="2547620241173881754">"پوشه جدید"</string>
     <string name="menu_grid" msgid="6878021334497835259">"نمای جدولی"</string>
     <string name="menu_list" msgid="7279285939892417279">"نمای فهرستی"</string>
     <string name="menu_sort" msgid="7677740407158414452">"مرتب‌سازی براساس"</string>
@@ -33,6 +33,7 @@
     <string name="menu_select_all" msgid="8323579667348729928">"انتخاب همه"</string>
     <string name="menu_copy" msgid="3612326052677229148">"کپی در..."</string>
     <string name="menu_move" msgid="1828090633118079817">"انتقال به…"</string>
+    <string name="menu_new_window" msgid="1226032889278727538">"پنجره جدید"</string>
     <string name="menu_copy_to_clipboard" msgid="489311381979634291">"کپی"</string>
     <string name="menu_paste_from_clipboard" msgid="2071583031180257091">"جای‌گذاری"</string>
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"نمایش فضای ذخیره‌سازی داخلی"</string>
@@ -45,8 +46,7 @@
     <string name="button_copy" msgid="8706475544635021302">"کپی"</string>
     <string name="button_move" msgid="2202666023104202232">"انتقال"</string>
     <string name="button_dismiss" msgid="3714065566893946085">"نپذیرفتن"</string>
-    <!-- no translation found for button_retry (4392027584153752797) -->
-    <skip />
+    <string name="button_retry" msgid="4392027584153752797">"دوباره امتحان کنید"</string>
     <string name="sort_name" msgid="9183560467917256779">"براساس نام"</string>
     <string name="sort_date" msgid="586080032956151448">"براساس تاریخ اصلاح"</string>
     <string name="sort_size" msgid="3350681319735474741">"براساس اندازه"</string>
diff --git a/packages/DocumentsUI/res/values-fi/strings.xml b/packages/DocumentsUI/res/values-fi/strings.xml
index 3e87300..33f2e3d 100644
--- a/packages/DocumentsUI/res/values-fi/strings.xml
+++ b/packages/DocumentsUI/res/values-fi/strings.xml
@@ -20,7 +20,7 @@
     <string name="files_label" msgid="6051402950202690279">"Tiedostot"</string>
     <string name="title_open" msgid="4353228937663917801">"Avaa sijainnista"</string>
     <string name="title_save" msgid="2433679664882857999">"Tallenna kohteeseen"</string>
-    <string name="menu_create_dir" msgid="5947289605844398389">"Luo kansio"</string>
+    <string name="menu_create_dir" msgid="2547620241173881754">"Uusi kansio"</string>
     <string name="menu_grid" msgid="6878021334497835259">"Ruudukkonäkymä"</string>
     <string name="menu_list" msgid="7279285939892417279">"Luettelonäkymä"</string>
     <string name="menu_sort" msgid="7677740407158414452">"Lajitteluperuste"</string>
@@ -33,6 +33,7 @@
     <string name="menu_select_all" msgid="8323579667348729928">"Valitse kaikki"</string>
     <string name="menu_copy" msgid="3612326052677229148">"Kopioi kohteeseen…"</string>
     <string name="menu_move" msgid="1828090633118079817">"Siirrä kohteeseen…"</string>
+    <string name="menu_new_window" msgid="1226032889278727538">"Uusi ikkuna"</string>
     <string name="menu_copy_to_clipboard" msgid="489311381979634291">"Kopioi"</string>
     <string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Liitä"</string>
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Näytä sis. tallennustila"</string>
@@ -45,8 +46,7 @@
     <string name="button_copy" msgid="8706475544635021302">"Kopioi"</string>
     <string name="button_move" msgid="2202666023104202232">"Siirrä"</string>
     <string name="button_dismiss" msgid="3714065566893946085">"Hylkää"</string>
-    <!-- no translation found for button_retry (4392027584153752797) -->
-    <skip />
+    <string name="button_retry" msgid="4392027584153752797">"Yritä uudelleen"</string>
     <string name="sort_name" msgid="9183560467917256779">"Nimen mukaan"</string>
     <string name="sort_date" msgid="586080032956151448">"Muokkauspäivän mukaan"</string>
     <string name="sort_size" msgid="3350681319735474741">"Koon mukaan"</string>
diff --git a/packages/DocumentsUI/res/values-fr-rCA/strings.xml b/packages/DocumentsUI/res/values-fr-rCA/strings.xml
index 3e82f34..228dc7a 100644
--- a/packages/DocumentsUI/res/values-fr-rCA/strings.xml
+++ b/packages/DocumentsUI/res/values-fr-rCA/strings.xml
@@ -20,7 +20,7 @@
     <string name="files_label" msgid="6051402950202690279">"Fichiers"</string>
     <string name="title_open" msgid="4353228937663917801">"Ouvrir à partir de"</string>
     <string name="title_save" msgid="2433679664882857999">"Enregistrer dans"</string>
-    <string name="menu_create_dir" msgid="5947289605844398389">"Créer un dossier"</string>
+    <string name="menu_create_dir" msgid="2547620241173881754">"Nouveau dossier"</string>
     <string name="menu_grid" msgid="6878021334497835259">"Grille"</string>
     <string name="menu_list" msgid="7279285939892417279">"Liste"</string>
     <string name="menu_sort" msgid="7677740407158414452">"Trier par"</string>
@@ -33,6 +33,7 @@
     <string name="menu_select_all" msgid="8323579667348729928">"Tout sélectionner"</string>
     <string name="menu_copy" msgid="3612326052677229148">"Copier vers..."</string>
     <string name="menu_move" msgid="1828090633118079817">"Déplacer dans…"</string>
+    <string name="menu_new_window" msgid="1226032889278727538">"Nouvelle fenêtre"</string>
     <string name="menu_copy_to_clipboard" msgid="489311381979634291">"Copier"</string>
     <string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Coller"</string>
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Aff. mém. stock. interne"</string>
@@ -45,8 +46,7 @@
     <string name="button_copy" msgid="8706475544635021302">"Copier"</string>
     <string name="button_move" msgid="2202666023104202232">"Déplacer"</string>
     <string name="button_dismiss" msgid="3714065566893946085">"Faire disparaître"</string>
-    <!-- no translation found for button_retry (4392027584153752797) -->
-    <skip />
+    <string name="button_retry" msgid="4392027584153752797">"Réessayer"</string>
     <string name="sort_name" msgid="9183560467917256779">"Par nom"</string>
     <string name="sort_date" msgid="586080032956151448">"Par date de modification"</string>
     <string name="sort_size" msgid="3350681319735474741">"Par taille"</string>
diff --git a/packages/DocumentsUI/res/values-fr/strings.xml b/packages/DocumentsUI/res/values-fr/strings.xml
index 0512ab1..eb9f84a 100644
--- a/packages/DocumentsUI/res/values-fr/strings.xml
+++ b/packages/DocumentsUI/res/values-fr/strings.xml
@@ -20,7 +20,7 @@
     <string name="files_label" msgid="6051402950202690279">"Fichiers"</string>
     <string name="title_open" msgid="4353228937663917801">"Ouvrir à partir de"</string>
     <string name="title_save" msgid="2433679664882857999">"Enregistrer sous"</string>
-    <string name="menu_create_dir" msgid="5947289605844398389">"Créer un dossier"</string>
+    <string name="menu_create_dir" msgid="2547620241173881754">"Nouveau dossier"</string>
     <string name="menu_grid" msgid="6878021334497835259">"Grille"</string>
     <string name="menu_list" msgid="7279285939892417279">"Liste"</string>
     <string name="menu_sort" msgid="7677740407158414452">"Trier par"</string>
@@ -33,6 +33,7 @@
     <string name="menu_select_all" msgid="8323579667348729928">"Tout sélectionner"</string>
     <string name="menu_copy" msgid="3612326052677229148">"Copier vers…"</string>
     <string name="menu_move" msgid="1828090633118079817">"Placer dans…"</string>
+    <string name="menu_new_window" msgid="1226032889278727538">"Nouvelle fenêtre"</string>
     <string name="menu_copy_to_clipboard" msgid="489311381979634291">"Copier"</string>
     <string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Coller"</string>
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Aff. mém. stock. interne"</string>
diff --git a/packages/DocumentsUI/res/values-gl-rES/strings.xml b/packages/DocumentsUI/res/values-gl-rES/strings.xml
index a1cd614..07c5d39 100644
--- a/packages/DocumentsUI/res/values-gl-rES/strings.xml
+++ b/packages/DocumentsUI/res/values-gl-rES/strings.xml
@@ -20,7 +20,7 @@
     <string name="files_label" msgid="6051402950202690279">"Ficheiros"</string>
     <string name="title_open" msgid="4353228937663917801">"Abrir desde"</string>
     <string name="title_save" msgid="2433679664882857999">"Gardar en"</string>
-    <string name="menu_create_dir" msgid="5947289605844398389">"Crear cartafol"</string>
+    <string name="menu_create_dir" msgid="2547620241173881754">"Novo cartafol"</string>
     <string name="menu_grid" msgid="6878021334497835259">"Vista de grade"</string>
     <string name="menu_list" msgid="7279285939892417279">"Vista de lista"</string>
     <string name="menu_sort" msgid="7677740407158414452">"Ordenar por"</string>
@@ -33,6 +33,7 @@
     <string name="menu_select_all" msgid="8323579667348729928">"Seleccionar todo"</string>
     <string name="menu_copy" msgid="3612326052677229148">"Copiar en…"</string>
     <string name="menu_move" msgid="1828090633118079817">"Mover a…"</string>
+    <string name="menu_new_window" msgid="1226032889278727538">"Nova ventá"</string>
     <string name="menu_copy_to_clipboard" msgid="489311381979634291">"Copiar"</string>
     <string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Pegar"</string>
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Mostrar almacen. interno"</string>
@@ -45,8 +46,7 @@
     <string name="button_copy" msgid="8706475544635021302">"Copiar"</string>
     <string name="button_move" msgid="2202666023104202232">"Mover"</string>
     <string name="button_dismiss" msgid="3714065566893946085">"Ignorar"</string>
-    <!-- no translation found for button_retry (4392027584153752797) -->
-    <skip />
+    <string name="button_retry" msgid="4392027584153752797">"Tentar de novo"</string>
     <string name="sort_name" msgid="9183560467917256779">"Por nome"</string>
     <string name="sort_date" msgid="586080032956151448">"Por data de modificación"</string>
     <string name="sort_size" msgid="3350681319735474741">"Por tamaño"</string>
diff --git a/packages/DocumentsUI/res/values-gu-rIN/strings.xml b/packages/DocumentsUI/res/values-gu-rIN/strings.xml
index 059fb2c..dade7df 100644
--- a/packages/DocumentsUI/res/values-gu-rIN/strings.xml
+++ b/packages/DocumentsUI/res/values-gu-rIN/strings.xml
@@ -20,7 +20,7 @@
     <string name="files_label" msgid="6051402950202690279">"ફાઇલો"</string>
     <string name="title_open" msgid="4353228937663917801">"અહીંથી ખોલો"</string>
     <string name="title_save" msgid="2433679664882857999">"આમાં સાચવો"</string>
-    <string name="menu_create_dir" msgid="5947289605844398389">"ફોલ્ડર બનાવો"</string>
+    <string name="menu_create_dir" msgid="2547620241173881754">"નવું ફોલ્ડર"</string>
     <string name="menu_grid" msgid="6878021334497835259">"ગ્રિડ દૃશ્ય"</string>
     <string name="menu_list" msgid="7279285939892417279">"સૂચિ દૃશ્ય"</string>
     <string name="menu_sort" msgid="7677740407158414452">"આ પ્રમાણે સૉર્ટ કરો"</string>
@@ -33,6 +33,7 @@
     <string name="menu_select_all" msgid="8323579667348729928">"બધા પસંદ કરો"</string>
     <string name="menu_copy" msgid="3612326052677229148">"આના પર કૉપિ કરો…"</string>
     <string name="menu_move" msgid="1828090633118079817">"આમાં ખસેડો…"</string>
+    <string name="menu_new_window" msgid="1226032889278727538">"નવી વિંડો"</string>
     <string name="menu_copy_to_clipboard" msgid="489311381979634291">"કૉપિ કરો"</string>
     <string name="menu_paste_from_clipboard" msgid="2071583031180257091">"પેસ્ટ કરો"</string>
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"આંતરિક સ્ટોરેજ બતાવો"</string>
diff --git a/packages/DocumentsUI/res/values-hi/strings.xml b/packages/DocumentsUI/res/values-hi/strings.xml
index 8e33a00..0a5de16 100644
--- a/packages/DocumentsUI/res/values-hi/strings.xml
+++ b/packages/DocumentsUI/res/values-hi/strings.xml
@@ -20,7 +20,7 @@
     <string name="files_label" msgid="6051402950202690279">"फ़ाइलें"</string>
     <string name="title_open" msgid="4353228937663917801">"यहां से खोलें"</string>
     <string name="title_save" msgid="2433679664882857999">"यहां सहेजें"</string>
-    <string name="menu_create_dir" msgid="5947289605844398389">"फ़ोल्डर बनाएं"</string>
+    <string name="menu_create_dir" msgid="2547620241173881754">"नया फ़ोल्डर"</string>
     <string name="menu_grid" msgid="6878021334497835259">"ग्रिड दृश्य"</string>
     <string name="menu_list" msgid="7279285939892417279">"सूची दृश्य"</string>
     <string name="menu_sort" msgid="7677740407158414452">"इससे क्रमित करें"</string>
@@ -33,6 +33,7 @@
     <string name="menu_select_all" msgid="8323579667348729928">"सभी चुनें"</string>
     <string name="menu_copy" msgid="3612326052677229148">"इनकी कॉपी बनाएं..."</string>
     <string name="menu_move" msgid="1828090633118079817">"इसमें ले जाएं…"</string>
+    <string name="menu_new_window" msgid="1226032889278727538">"नई विंडो"</string>
     <string name="menu_copy_to_clipboard" msgid="489311381979634291">"कॉपी करें"</string>
     <string name="menu_paste_from_clipboard" msgid="2071583031180257091">"चिपकाएं"</string>
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"आंतरिक मेमोरी दिखाएं"</string>
diff --git a/packages/DocumentsUI/res/values-hr/strings.xml b/packages/DocumentsUI/res/values-hr/strings.xml
index 4774753..78c1485 100644
--- a/packages/DocumentsUI/res/values-hr/strings.xml
+++ b/packages/DocumentsUI/res/values-hr/strings.xml
@@ -20,7 +20,7 @@
     <string name="files_label" msgid="6051402950202690279">"Datoteke"</string>
     <string name="title_open" msgid="4353228937663917801">"Otvori iz"</string>
     <string name="title_save" msgid="2433679664882857999">"Spremi u"</string>
-    <string name="menu_create_dir" msgid="5947289605844398389">"Izradi mapu"</string>
+    <string name="menu_create_dir" msgid="2547620241173881754">"Nova mapa"</string>
     <string name="menu_grid" msgid="6878021334497835259">"Prikaz rešetke"</string>
     <string name="menu_list" msgid="7279285939892417279">"Prikaz popisa"</string>
     <string name="menu_sort" msgid="7677740407158414452">"Poredano po"</string>
@@ -33,6 +33,7 @@
     <string name="menu_select_all" msgid="8323579667348729928">"Odaberi sve"</string>
     <string name="menu_copy" msgid="3612326052677229148">"Kopiraj u…"</string>
     <string name="menu_move" msgid="1828090633118079817">"Premjesti u…"</string>
+    <string name="menu_new_window" msgid="1226032889278727538">"Novi prozor"</string>
     <string name="menu_copy_to_clipboard" msgid="489311381979634291">"Kopiraj"</string>
     <string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Zalijepi"</string>
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Pokaži internu pohranu"</string>
@@ -45,8 +46,7 @@
     <string name="button_copy" msgid="8706475544635021302">"Kopiraj"</string>
     <string name="button_move" msgid="2202666023104202232">"Premjesti"</string>
     <string name="button_dismiss" msgid="3714065566893946085">"Odbaci"</string>
-    <!-- no translation found for button_retry (4392027584153752797) -->
-    <skip />
+    <string name="button_retry" msgid="4392027584153752797">"Pokušaj ponovo"</string>
     <string name="sort_name" msgid="9183560467917256779">"Po nazivu"</string>
     <string name="sort_date" msgid="586080032956151448">"Po datumu izmjene"</string>
     <string name="sort_size" msgid="3350681319735474741">"Po veličini"</string>
diff --git a/packages/DocumentsUI/res/values-hu/strings.xml b/packages/DocumentsUI/res/values-hu/strings.xml
index 7a521ec..4948116 100644
--- a/packages/DocumentsUI/res/values-hu/strings.xml
+++ b/packages/DocumentsUI/res/values-hu/strings.xml
@@ -20,7 +20,7 @@
     <string name="files_label" msgid="6051402950202690279">"Fájlok"</string>
     <string name="title_open" msgid="4353228937663917801">"Megnyitás innen"</string>
     <string name="title_save" msgid="2433679664882857999">"Mentés ide"</string>
-    <string name="menu_create_dir" msgid="5947289605844398389">"Mappa létrehozása"</string>
+    <string name="menu_create_dir" msgid="2547620241173881754">"Új mappa"</string>
     <string name="menu_grid" msgid="6878021334497835259">"Rács"</string>
     <string name="menu_list" msgid="7279285939892417279">"Lista"</string>
     <string name="menu_sort" msgid="7677740407158414452">"Rendezés"</string>
@@ -33,6 +33,7 @@
     <string name="menu_select_all" msgid="8323579667348729928">"Összes kijelölése"</string>
     <string name="menu_copy" msgid="3612326052677229148">"Másolás ide…"</string>
     <string name="menu_move" msgid="1828090633118079817">"Áthelyezés…"</string>
+    <string name="menu_new_window" msgid="1226032889278727538">"Új ablak"</string>
     <string name="menu_copy_to_clipboard" msgid="489311381979634291">"Másolás"</string>
     <string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Beillesztés"</string>
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Belső tárhely"</string>
@@ -45,8 +46,7 @@
     <string name="button_copy" msgid="8706475544635021302">"Másolás"</string>
     <string name="button_move" msgid="2202666023104202232">"Áthelyezés"</string>
     <string name="button_dismiss" msgid="3714065566893946085">"Elvetés"</string>
-    <!-- no translation found for button_retry (4392027584153752797) -->
-    <skip />
+    <string name="button_retry" msgid="4392027584153752797">"Újrapróbálkozás"</string>
     <string name="sort_name" msgid="9183560467917256779">"Név szerint"</string>
     <string name="sort_date" msgid="586080032956151448">"Módosítás dátuma szerint"</string>
     <string name="sort_size" msgid="3350681319735474741">"Méret szerint"</string>
diff --git a/packages/DocumentsUI/res/values-hy-rAM/strings.xml b/packages/DocumentsUI/res/values-hy-rAM/strings.xml
index 95d73f0..6dc66b0 100644
--- a/packages/DocumentsUI/res/values-hy-rAM/strings.xml
+++ b/packages/DocumentsUI/res/values-hy-rAM/strings.xml
@@ -20,7 +20,7 @@
     <string name="files_label" msgid="6051402950202690279">"Ֆայլեր"</string>
     <string name="title_open" msgid="4353228937663917801">"Բացել այստեղից"</string>
     <string name="title_save" msgid="2433679664882857999">"Պահել այստեղ"</string>
-    <string name="menu_create_dir" msgid="5947289605844398389">"Ստեղծել պանակ"</string>
+    <string name="menu_create_dir" msgid="2547620241173881754">"Նոր պանակ"</string>
     <string name="menu_grid" msgid="6878021334497835259">"Ցանցի տեսքով"</string>
     <string name="menu_list" msgid="7279285939892417279">"Ցուցակի տեսք"</string>
     <string name="menu_sort" msgid="7677740407158414452">"Դասավորել ըստ"</string>
@@ -33,6 +33,7 @@
     <string name="menu_select_all" msgid="8323579667348729928">"Ընտրել բոլորը"</string>
     <string name="menu_copy" msgid="3612326052677229148">"Պատճենել…"</string>
     <string name="menu_move" msgid="1828090633118079817">"Տեղափոխում դեպի…"</string>
+    <string name="menu_new_window" msgid="1226032889278727538">"Նոր պատուհան"</string>
     <string name="menu_copy_to_clipboard" msgid="489311381979634291">"Պատճենել"</string>
     <string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Տեղադրել"</string>
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Ցույց տալ ներքին պահոցը"</string>
@@ -45,8 +46,7 @@
     <string name="button_copy" msgid="8706475544635021302">"Պատճենել"</string>
     <string name="button_move" msgid="2202666023104202232">"Տեղափոխել"</string>
     <string name="button_dismiss" msgid="3714065566893946085">"Փակել"</string>
-    <!-- no translation found for button_retry (4392027584153752797) -->
-    <skip />
+    <string name="button_retry" msgid="4392027584153752797">"Փորձել նորից"</string>
     <string name="sort_name" msgid="9183560467917256779">"Ըստ անվան"</string>
     <string name="sort_date" msgid="586080032956151448">"Ըստ փոփոխման ամսաթվի"</string>
     <string name="sort_size" msgid="3350681319735474741">"Ըստ չափի"</string>
diff --git a/packages/DocumentsUI/res/values-in/strings.xml b/packages/DocumentsUI/res/values-in/strings.xml
index 63d415c..2412488 100644
--- a/packages/DocumentsUI/res/values-in/strings.xml
+++ b/packages/DocumentsUI/res/values-in/strings.xml
@@ -20,7 +20,7 @@
     <string name="files_label" msgid="6051402950202690279">"File"</string>
     <string name="title_open" msgid="4353228937663917801">"Buka dari"</string>
     <string name="title_save" msgid="2433679664882857999">"Simpan ke"</string>
-    <string name="menu_create_dir" msgid="5947289605844398389">"Buat folder"</string>
+    <string name="menu_create_dir" msgid="2547620241173881754">"Folder baru"</string>
     <string name="menu_grid" msgid="6878021334497835259">"Tampilan kisi"</string>
     <string name="menu_list" msgid="7279285939892417279">"Tampilan daftar"</string>
     <string name="menu_sort" msgid="7677740407158414452">"Urutkan menurut"</string>
@@ -33,6 +33,7 @@
     <string name="menu_select_all" msgid="8323579667348729928">"Pilih semua"</string>
     <string name="menu_copy" msgid="3612326052677229148">"Salin ke…"</string>
     <string name="menu_move" msgid="1828090633118079817">"Pindahkan ke..."</string>
+    <string name="menu_new_window" msgid="1226032889278727538">"Jendela baru"</string>
     <string name="menu_copy_to_clipboard" msgid="489311381979634291">"Salin"</string>
     <string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Tempel"</string>
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Tampilkan simpanan internal"</string>
diff --git a/packages/DocumentsUI/res/values-is-rIS/strings.xml b/packages/DocumentsUI/res/values-is-rIS/strings.xml
index 0c0e47f..d09a44b 100644
--- a/packages/DocumentsUI/res/values-is-rIS/strings.xml
+++ b/packages/DocumentsUI/res/values-is-rIS/strings.xml
@@ -20,7 +20,7 @@
     <string name="files_label" msgid="6051402950202690279">"Skrár"</string>
     <string name="title_open" msgid="4353228937663917801">"Opna frá"</string>
     <string name="title_save" msgid="2433679664882857999">"Vista í"</string>
-    <string name="menu_create_dir" msgid="5947289605844398389">"Búa til möppu"</string>
+    <string name="menu_create_dir" msgid="2547620241173881754">"Ný mappa"</string>
     <string name="menu_grid" msgid="6878021334497835259">"Töfluyfirlit"</string>
     <string name="menu_list" msgid="7279285939892417279">"Listayfirlit"</string>
     <string name="menu_sort" msgid="7677740407158414452">"Raða eftir"</string>
@@ -33,6 +33,7 @@
     <string name="menu_select_all" msgid="8323579667348729928">"Velja allt"</string>
     <string name="menu_copy" msgid="3612326052677229148">"Afrita í ..."</string>
     <string name="menu_move" msgid="1828090633118079817">"Færa í…"</string>
+    <string name="menu_new_window" msgid="1226032889278727538">"Nýr gluggi"</string>
     <string name="menu_copy_to_clipboard" msgid="489311381979634291">"Afrita"</string>
     <string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Líma"</string>
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Sýna innbyggða geymslu"</string>
diff --git a/packages/DocumentsUI/res/values-it/strings.xml b/packages/DocumentsUI/res/values-it/strings.xml
index de129a7..4824205 100644
--- a/packages/DocumentsUI/res/values-it/strings.xml
+++ b/packages/DocumentsUI/res/values-it/strings.xml
@@ -20,7 +20,7 @@
     <string name="files_label" msgid="6051402950202690279">"File"</string>
     <string name="title_open" msgid="4353228937663917801">"Apri da"</string>
     <string name="title_save" msgid="2433679664882857999">"Salva in"</string>
-    <string name="menu_create_dir" msgid="5947289605844398389">"Crea cartella"</string>
+    <string name="menu_create_dir" msgid="2547620241173881754">"Nuova cartella"</string>
     <string name="menu_grid" msgid="6878021334497835259">"Visualizzazione griglia"</string>
     <string name="menu_list" msgid="7279285939892417279">"Visualizzazione elenco"</string>
     <string name="menu_sort" msgid="7677740407158414452">"Ordina per"</string>
@@ -33,6 +33,7 @@
     <string name="menu_select_all" msgid="8323579667348729928">"Seleziona tutto"</string>
     <string name="menu_copy" msgid="3612326052677229148">"Copia in…"</string>
     <string name="menu_move" msgid="1828090633118079817">"Sposta in..."</string>
+    <string name="menu_new_window" msgid="1226032889278727538">"Nuova finestra"</string>
     <string name="menu_copy_to_clipboard" msgid="489311381979634291">"Copia"</string>
     <string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Incolla"</string>
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Mostra memoria interna"</string>
diff --git a/packages/DocumentsUI/res/values-iw/strings.xml b/packages/DocumentsUI/res/values-iw/strings.xml
index dbe4630..1a19510 100644
--- a/packages/DocumentsUI/res/values-iw/strings.xml
+++ b/packages/DocumentsUI/res/values-iw/strings.xml
@@ -20,7 +20,7 @@
     <string name="files_label" msgid="6051402950202690279">"קבצים"</string>
     <string name="title_open" msgid="4353228937663917801">"פתח מ-"</string>
     <string name="title_save" msgid="2433679664882857999">"שמור ב-"</string>
-    <string name="menu_create_dir" msgid="5947289605844398389">"צור תיקיה"</string>
+    <string name="menu_create_dir" msgid="2547620241173881754">"תיקייה חדשה"</string>
     <string name="menu_grid" msgid="6878021334497835259">"תצוגת רשת"</string>
     <string name="menu_list" msgid="7279285939892417279">"תצוגת רשימה"</string>
     <string name="menu_sort" msgid="7677740407158414452">"מיין לפי"</string>
@@ -33,6 +33,7 @@
     <string name="menu_select_all" msgid="8323579667348729928">"בחר הכל"</string>
     <string name="menu_copy" msgid="3612326052677229148">"העתק אל…"</string>
     <string name="menu_move" msgid="1828090633118079817">"העברה אל…"</string>
+    <string name="menu_new_window" msgid="1226032889278727538">"חלון חדש"</string>
     <string name="menu_copy_to_clipboard" msgid="489311381979634291">"העתק"</string>
     <string name="menu_paste_from_clipboard" msgid="2071583031180257091">"הדבק"</string>
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"הצג אחסון פנימי"</string>
@@ -45,8 +46,7 @@
     <string name="button_copy" msgid="8706475544635021302">"העתק"</string>
     <string name="button_move" msgid="2202666023104202232">"העבר"</string>
     <string name="button_dismiss" msgid="3714065566893946085">"הסר"</string>
-    <!-- no translation found for button_retry (4392027584153752797) -->
-    <skip />
+    <string name="button_retry" msgid="4392027584153752797">"נסה שוב"</string>
     <string name="sort_name" msgid="9183560467917256779">"לפי שם"</string>
     <string name="sort_date" msgid="586080032956151448">"לפי תאריך שינוי"</string>
     <string name="sort_size" msgid="3350681319735474741">"לפי גודל"</string>
diff --git a/packages/DocumentsUI/res/values-ja/strings.xml b/packages/DocumentsUI/res/values-ja/strings.xml
index 45f0ea1..7e356af 100644
--- a/packages/DocumentsUI/res/values-ja/strings.xml
+++ b/packages/DocumentsUI/res/values-ja/strings.xml
@@ -20,7 +20,7 @@
     <string name="files_label" msgid="6051402950202690279">"ファイル"</string>
     <string name="title_open" msgid="4353228937663917801">"次から開く:"</string>
     <string name="title_save" msgid="2433679664882857999">"次に保存:"</string>
-    <string name="menu_create_dir" msgid="5947289605844398389">"フォルダを作成"</string>
+    <string name="menu_create_dir" msgid="2547620241173881754">"新しいフォルダ"</string>
     <string name="menu_grid" msgid="6878021334497835259">"グリッド表示"</string>
     <string name="menu_list" msgid="7279285939892417279">"リスト表示"</string>
     <string name="menu_sort" msgid="7677740407158414452">"並べ替え"</string>
@@ -33,6 +33,7 @@
     <string name="menu_select_all" msgid="8323579667348729928">"すべて選択"</string>
     <string name="menu_copy" msgid="3612326052677229148">"コピー…"</string>
     <string name="menu_move" msgid="1828090633118079817">"移動..."</string>
+    <string name="menu_new_window" msgid="1226032889278727538">"新しいウィンドウ"</string>
     <string name="menu_copy_to_clipboard" msgid="489311381979634291">"コピー"</string>
     <string name="menu_paste_from_clipboard" msgid="2071583031180257091">"貼り付け"</string>
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"内部ストレージを表示"</string>
@@ -45,8 +46,7 @@
     <string name="button_copy" msgid="8706475544635021302">"コピー"</string>
     <string name="button_move" msgid="2202666023104202232">"移動"</string>
     <string name="button_dismiss" msgid="3714065566893946085">"表示しない"</string>
-    <!-- no translation found for button_retry (4392027584153752797) -->
-    <skip />
+    <string name="button_retry" msgid="4392027584153752797">"再試行"</string>
     <string name="sort_name" msgid="9183560467917256779">"名前順"</string>
     <string name="sort_date" msgid="586080032956151448">"更新日順"</string>
     <string name="sort_size" msgid="3350681319735474741">"サイズ順"</string>
diff --git a/packages/DocumentsUI/res/values-ka-rGE/strings.xml b/packages/DocumentsUI/res/values-ka-rGE/strings.xml
index 114e73c..6e880aa 100644
--- a/packages/DocumentsUI/res/values-ka-rGE/strings.xml
+++ b/packages/DocumentsUI/res/values-ka-rGE/strings.xml
@@ -20,7 +20,7 @@
     <string name="files_label" msgid="6051402950202690279">"ფაილები"</string>
     <string name="title_open" msgid="4353228937663917801">"გახსნა აქედან:"</string>
     <string name="title_save" msgid="2433679664882857999">"შენახვა აქ:"</string>
-    <string name="menu_create_dir" msgid="5947289605844398389">"საქაღალდის შექმნა"</string>
+    <string name="menu_create_dir" msgid="2547620241173881754">"ახალი საქაღალდე"</string>
     <string name="menu_grid" msgid="6878021334497835259">"ბადის ხედი"</string>
     <string name="menu_list" msgid="7279285939892417279">"სიის ხედი"</string>
     <string name="menu_sort" msgid="7677740407158414452">"სორტირება:"</string>
@@ -33,6 +33,7 @@
     <string name="menu_select_all" msgid="8323579667348729928">"ყველას არჩევა"</string>
     <string name="menu_copy" msgid="3612326052677229148">"კოპირება…"</string>
     <string name="menu_move" msgid="1828090633118079817">"გადაადგილება..."</string>
+    <string name="menu_new_window" msgid="1226032889278727538">"ახალი ფანჯარა"</string>
     <string name="menu_copy_to_clipboard" msgid="489311381979634291">"კოპირება"</string>
     <string name="menu_paste_from_clipboard" msgid="2071583031180257091">"ჩასმა"</string>
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"შიდა საცავის ჩვენება"</string>
@@ -45,8 +46,7 @@
     <string name="button_copy" msgid="8706475544635021302">"კოპირება"</string>
     <string name="button_move" msgid="2202666023104202232">"გადაადგილება"</string>
     <string name="button_dismiss" msgid="3714065566893946085">"დახურვა"</string>
-    <!-- no translation found for button_retry (4392027584153752797) -->
-    <skip />
+    <string name="button_retry" msgid="4392027584153752797">"ისევ ცდა"</string>
     <string name="sort_name" msgid="9183560467917256779">"სახელით"</string>
     <string name="sort_date" msgid="586080032956151448">"ცვლილების თარიღით"</string>
     <string name="sort_size" msgid="3350681319735474741">"ზომით"</string>
diff --git a/packages/DocumentsUI/res/values-kk-rKZ/strings.xml b/packages/DocumentsUI/res/values-kk-rKZ/strings.xml
index af123c6..0d553e0 100644
--- a/packages/DocumentsUI/res/values-kk-rKZ/strings.xml
+++ b/packages/DocumentsUI/res/values-kk-rKZ/strings.xml
@@ -20,7 +20,7 @@
     <string name="files_label" msgid="6051402950202690279">"Файлдар"</string>
     <string name="title_open" msgid="4353228937663917801">"Мынадан ашу:"</string>
     <string name="title_save" msgid="2433679664882857999">"Сақталатын орны"</string>
-    <string name="menu_create_dir" msgid="5947289605844398389">"Қалта жасақтау"</string>
+    <string name="menu_create_dir" msgid="2547620241173881754">"Жаңа қалта"</string>
     <string name="menu_grid" msgid="6878021334497835259">"Торлы көрініс"</string>
     <string name="menu_list" msgid="7279285939892417279">"Тізім көрінісі"</string>
     <string name="menu_sort" msgid="7677740407158414452">"Белгіге қарай сұрыптау"</string>
@@ -33,6 +33,7 @@
     <string name="menu_select_all" msgid="8323579667348729928">"Барлығын таңдау"</string>
     <string name="menu_copy" msgid="3612326052677229148">"Көшіру орны…"</string>
     <string name="menu_move" msgid="1828090633118079817">"Орнын ауыстыру…"</string>
+    <string name="menu_new_window" msgid="1226032889278727538">"Жаңа терезе"</string>
     <string name="menu_copy_to_clipboard" msgid="489311381979634291">"Көшіру"</string>
     <string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Қою"</string>
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Ішкі жадты көрсету"</string>
@@ -45,8 +46,7 @@
     <string name="button_copy" msgid="8706475544635021302">"Көшіру"</string>
     <string name="button_move" msgid="2202666023104202232">"Жылжыту"</string>
     <string name="button_dismiss" msgid="3714065566893946085">"Өшіру"</string>
-    <!-- no translation found for button_retry (4392027584153752797) -->
-    <skip />
+    <string name="button_retry" msgid="4392027584153752797">"Әрекетті қайталау"</string>
     <string name="sort_name" msgid="9183560467917256779">"Атауы бойынша"</string>
     <string name="sort_date" msgid="586080032956151448">"Өзгертілген мерзімі бойынша"</string>
     <string name="sort_size" msgid="3350681319735474741">"Өлшемі бойынша"</string>
diff --git a/packages/DocumentsUI/res/values-km-rKH/strings.xml b/packages/DocumentsUI/res/values-km-rKH/strings.xml
index 8f3feae..4680879 100644
--- a/packages/DocumentsUI/res/values-km-rKH/strings.xml
+++ b/packages/DocumentsUI/res/values-km-rKH/strings.xml
@@ -20,7 +20,7 @@
     <string name="files_label" msgid="6051402950202690279">"ឯកសារ"</string>
     <string name="title_open" msgid="4353228937663917801">"បើក​ពី"</string>
     <string name="title_save" msgid="2433679664882857999">"រក្សា​ទុក​ទៅ"</string>
-    <string name="menu_create_dir" msgid="5947289605844398389">"បង្កើត​ថត"</string>
+    <string name="menu_create_dir" msgid="2547620241173881754">"ថត​ថ្មី"</string>
     <string name="menu_grid" msgid="6878021334497835259">"ទិដ្ឋភាព​ក្រឡា​"</string>
     <string name="menu_list" msgid="7279285939892417279">"ទិដ្ឋភាព​បញ្ជី"</string>
     <string name="menu_sort" msgid="7677740407158414452">"តម្រៀប​តាម"</string>
@@ -33,6 +33,7 @@
     <string name="menu_select_all" msgid="8323579667348729928">"ជ្រើសរើសទាំងអស់"</string>
     <string name="menu_copy" msgid="3612326052677229148">"ថតចម្លងទៅ…"</string>
     <string name="menu_move" msgid="1828090633118079817">"ផ្លាស់ទីទៅ៖"</string>
+    <string name="menu_new_window" msgid="1226032889278727538">"បង្អួចថ្មី"</string>
     <string name="menu_copy_to_clipboard" msgid="489311381979634291">"ចម្លង"</string>
     <string name="menu_paste_from_clipboard" msgid="2071583031180257091">"បិទភ្ជាប់"</string>
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"បង្ហាញឧបករណ៍ផ្ទុកខាងក្នុង"</string>
@@ -45,8 +46,7 @@
     <string name="button_copy" msgid="8706475544635021302">"ចម្លង"</string>
     <string name="button_move" msgid="2202666023104202232">"ផ្លាស់ទី"</string>
     <string name="button_dismiss" msgid="3714065566893946085">"បដិសេធ"</string>
-    <!-- no translation found for button_retry (4392027584153752797) -->
-    <skip />
+    <string name="button_retry" msgid="4392027584153752797">"ព្យាយាមម្ដងទៀត"</string>
     <string name="sort_name" msgid="9183560467917256779">"តាម​ឈ្មោះ"</string>
     <string name="sort_date" msgid="586080032956151448">"តាម​កាលបរិច្ឆេទ​បាន​កែប្រែ"</string>
     <string name="sort_size" msgid="3350681319735474741">"តាម​​ទំហំ"</string>
diff --git a/packages/DocumentsUI/res/values-kn-rIN/strings.xml b/packages/DocumentsUI/res/values-kn-rIN/strings.xml
index 826cf1e..7d5770a 100644
--- a/packages/DocumentsUI/res/values-kn-rIN/strings.xml
+++ b/packages/DocumentsUI/res/values-kn-rIN/strings.xml
@@ -20,7 +20,7 @@
     <string name="files_label" msgid="6051402950202690279">"ಫೈಲ್‌ಗಳು"</string>
     <string name="title_open" msgid="4353228937663917801">"ಇದರ ಮೂಲಕ ತೆರೆಯಿರಿ"</string>
     <string name="title_save" msgid="2433679664882857999">"ಇವುಗಳಲ್ಲಿ ಉಳಿಸಿ"</string>
-    <string name="menu_create_dir" msgid="5947289605844398389">"ಫೋಲ್ಡರ್ ರಚಿಸು"</string>
+    <string name="menu_create_dir" msgid="2547620241173881754">"ಹೊಸ ಫೋಲ್ಡರ್"</string>
     <string name="menu_grid" msgid="6878021334497835259">"ಗ್ರಿಡ್ ವೀಕ್ಷಣೆ"</string>
     <string name="menu_list" msgid="7279285939892417279">"ಪಟ್ಟಿ ವೀಕ್ಷಣೆ"</string>
     <string name="menu_sort" msgid="7677740407158414452">"ಈ ಪ್ರಕಾರ ವಿಂಗಡಿಸು"</string>
@@ -33,6 +33,7 @@
     <string name="menu_select_all" msgid="8323579667348729928">"ಎಲ್ಲವನ್ನೂ ಆಯ್ಕೆಮಾಡಿ"</string>
     <string name="menu_copy" msgid="3612326052677229148">"ಇದಕ್ಕೆ ನಕಲಿಸಿ…"</string>
     <string name="menu_move" msgid="1828090633118079817">"ಇದಕ್ಕೆ ಸರಿಸು…"</string>
+    <string name="menu_new_window" msgid="1226032889278727538">"ಹೊಸ ವಿಂಡೋ"</string>
     <string name="menu_copy_to_clipboard" msgid="489311381979634291">"ನಕಲಿಸು"</string>
     <string name="menu_paste_from_clipboard" msgid="2071583031180257091">"ಅಂಟಿಸು"</string>
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"ಆಂತರಿಕ ಸಂಗ್ರಹಣೆಯನ್ನು ತೋರಿಸು"</string>
diff --git a/packages/DocumentsUI/res/values-ko/strings.xml b/packages/DocumentsUI/res/values-ko/strings.xml
index 322b43b..3032eed 100644
--- a/packages/DocumentsUI/res/values-ko/strings.xml
+++ b/packages/DocumentsUI/res/values-ko/strings.xml
@@ -20,7 +20,7 @@
     <string name="files_label" msgid="6051402950202690279">"파일"</string>
     <string name="title_open" msgid="4353228937663917801">"열기:"</string>
     <string name="title_save" msgid="2433679664882857999">"저장 위치:"</string>
-    <string name="menu_create_dir" msgid="5947289605844398389">"폴더 만들기"</string>
+    <string name="menu_create_dir" msgid="2547620241173881754">"새 폴더"</string>
     <string name="menu_grid" msgid="6878021334497835259">"바둑판식 보기"</string>
     <string name="menu_list" msgid="7279285939892417279">"목록 보기"</string>
     <string name="menu_sort" msgid="7677740407158414452">"정렬 기준"</string>
@@ -33,6 +33,7 @@
     <string name="menu_select_all" msgid="8323579667348729928">"모두 선택"</string>
     <string name="menu_copy" msgid="3612326052677229148">"복사…"</string>
     <string name="menu_move" msgid="1828090633118079817">"이동…"</string>
+    <string name="menu_new_window" msgid="1226032889278727538">"새 창"</string>
     <string name="menu_copy_to_clipboard" msgid="489311381979634291">"복사"</string>
     <string name="menu_paste_from_clipboard" msgid="2071583031180257091">"붙여넣기"</string>
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"내부 저장소 표시"</string>
@@ -45,8 +46,7 @@
     <string name="button_copy" msgid="8706475544635021302">"복사"</string>
     <string name="button_move" msgid="2202666023104202232">"이동"</string>
     <string name="button_dismiss" msgid="3714065566893946085">"닫기"</string>
-    <!-- no translation found for button_retry (4392027584153752797) -->
-    <skip />
+    <string name="button_retry" msgid="4392027584153752797">"다시 시도"</string>
     <string name="sort_name" msgid="9183560467917256779">"이름순"</string>
     <string name="sort_date" msgid="586080032956151448">"수정된 날짜순"</string>
     <string name="sort_size" msgid="3350681319735474741">"크기순"</string>
diff --git a/packages/DocumentsUI/res/values-ky-rKG/strings.xml b/packages/DocumentsUI/res/values-ky-rKG/strings.xml
index c8aa69e..14a25bc 100644
--- a/packages/DocumentsUI/res/values-ky-rKG/strings.xml
+++ b/packages/DocumentsUI/res/values-ky-rKG/strings.xml
@@ -20,7 +20,7 @@
     <string name="files_label" msgid="6051402950202690279">"Файлдар"</string>
     <string name="title_open" msgid="4353228937663917801">"Кийинкиден ачуу:"</string>
     <string name="title_save" msgid="2433679664882857999">"Кийинкиге сактоо:"</string>
-    <string name="menu_create_dir" msgid="5947289605844398389">"Папка түзүү"</string>
+    <string name="menu_create_dir" msgid="2547620241173881754">"Жаңы куржун"</string>
     <string name="menu_grid" msgid="6878021334497835259">"Тор көрүнүшү"</string>
     <string name="menu_list" msgid="7279285939892417279">"Тизмек көрүнүшү"</string>
     <string name="menu_sort" msgid="7677740407158414452">"Ылгоо"</string>
@@ -33,6 +33,7 @@
     <string name="menu_select_all" msgid="8323579667348729928">"Бардыгын тандоо"</string>
     <string name="menu_copy" msgid="3612326052677229148">"Төмөнкүгө көчүрүү…"</string>
     <string name="menu_move" msgid="1828090633118079817">"Төмөнкүгө жылдыруу..."</string>
+    <string name="menu_new_window" msgid="1226032889278727538">"Жаңы терезе"</string>
     <string name="menu_copy_to_clipboard" msgid="489311381979634291">"Көчүрүү"</string>
     <string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Чаптоо"</string>
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Ички сактагычты көрсөтүү"</string>
@@ -45,8 +46,7 @@
     <string name="button_copy" msgid="8706475544635021302">"Көчүрүү"</string>
     <string name="button_move" msgid="2202666023104202232">"Жылдыруу"</string>
     <string name="button_dismiss" msgid="3714065566893946085">"Этибарга албоо"</string>
-    <!-- no translation found for button_retry (4392027584153752797) -->
-    <skip />
+    <string name="button_retry" msgid="4392027584153752797">"Дагы аракет кылыңыз"</string>
     <string name="sort_name" msgid="9183560467917256779">"Аты боюнча"</string>
     <string name="sort_date" msgid="586080032956151448">"Өзгөртүлгөн күнү боюнча"</string>
     <string name="sort_size" msgid="3350681319735474741">"Өлчөмү боюнча"</string>
diff --git a/packages/DocumentsUI/res/values-lo-rLA/strings.xml b/packages/DocumentsUI/res/values-lo-rLA/strings.xml
index dedfa05..2a680bf 100644
--- a/packages/DocumentsUI/res/values-lo-rLA/strings.xml
+++ b/packages/DocumentsUI/res/values-lo-rLA/strings.xml
@@ -20,7 +20,7 @@
     <string name="files_label" msgid="6051402950202690279">"​ໄຟລ໌"</string>
     <string name="title_open" msgid="4353228937663917801">"ເປີດ​ຈາກ"</string>
     <string name="title_save" msgid="2433679664882857999">"ບັນທຶກໄປທີ່"</string>
-    <string name="menu_create_dir" msgid="5947289605844398389">"ສ້າງໂຟນເດີ"</string>
+    <string name="menu_create_dir" msgid="2547620241173881754">"ໂຟ​ລ​ເດີໃໝ່"</string>
     <string name="menu_grid" msgid="6878021334497835259">"ມຸມມອງແບບຊ່ອງ"</string>
     <string name="menu_list" msgid="7279285939892417279">"ມຸມມອງແບບລາຍຊື່"</string>
     <string name="menu_sort" msgid="7677740407158414452">"ຮຽງລຳດັບຕາມ"</string>
@@ -33,6 +33,7 @@
     <string name="menu_select_all" msgid="8323579667348729928">"ເລືອກທັງຫມົດ"</string>
     <string name="menu_copy" msgid="3612326052677229148">"ອັດ​ສຳ​ເນົາ​ໃສ່…"</string>
     <string name="menu_move" msgid="1828090633118079817">"ຍ້າຍໄປໃສ່..."</string>
+    <string name="menu_new_window" msgid="1226032889278727538">"ໜ້າຈໍໃໝ່"</string>
     <string name="menu_copy_to_clipboard" msgid="489311381979634291">"ສຳເນົາ"</string>
     <string name="menu_paste_from_clipboard" msgid="2071583031180257091">"​ວາງໃສ່"</string>
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"ສະແດງ​ໂຕເກັບ​ຂໍ້ມູນພາຍໃນ"</string>
@@ -45,8 +46,7 @@
     <string name="button_copy" msgid="8706475544635021302">"ສຳເນົາ"</string>
     <string name="button_move" msgid="2202666023104202232">"ຍ້າຍ"</string>
     <string name="button_dismiss" msgid="3714065566893946085">"ປິດໄວ້"</string>
-    <!-- no translation found for button_retry (4392027584153752797) -->
-    <skip />
+    <string name="button_retry" msgid="4392027584153752797">"​ລອງ​ໃໝ່​ອີກ​"</string>
     <string name="sort_name" msgid="9183560467917256779">"ຕາມຊື່"</string>
     <string name="sort_date" msgid="586080032956151448">"ຕາມວັນທີທີ່ແກ້ໄຂ"</string>
     <string name="sort_size" msgid="3350681319735474741">"ຕາມຂະໜາດ"</string>
diff --git a/packages/DocumentsUI/res/values-lt/strings.xml b/packages/DocumentsUI/res/values-lt/strings.xml
index 0c8b443..8105846 100644
--- a/packages/DocumentsUI/res/values-lt/strings.xml
+++ b/packages/DocumentsUI/res/values-lt/strings.xml
@@ -20,7 +20,7 @@
     <string name="files_label" msgid="6051402950202690279">"Failai"</string>
     <string name="title_open" msgid="4353228937663917801">"Atidaryti iš"</string>
     <string name="title_save" msgid="2433679664882857999">"Išsaugoti į"</string>
-    <string name="menu_create_dir" msgid="5947289605844398389">"Kurti aplanką"</string>
+    <string name="menu_create_dir" msgid="2547620241173881754">"Naujas aplankas"</string>
     <string name="menu_grid" msgid="6878021334497835259">"Tinklelio rodinys"</string>
     <string name="menu_list" msgid="7279285939892417279">"Sąrašo rodinys"</string>
     <string name="menu_sort" msgid="7677740407158414452">"Rūšiuoti pagal"</string>
@@ -33,6 +33,7 @@
     <string name="menu_select_all" msgid="8323579667348729928">"Pasirinkti viską"</string>
     <string name="menu_copy" msgid="3612326052677229148">"Kopijuoti į..."</string>
     <string name="menu_move" msgid="1828090633118079817">"Perkelti į…"</string>
+    <string name="menu_new_window" msgid="1226032889278727538">"Naujas langas"</string>
     <string name="menu_copy_to_clipboard" msgid="489311381979634291">"Kopijuoti"</string>
     <string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Įklijuoti"</string>
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Rodyti vidinę atmintį"</string>
@@ -45,8 +46,7 @@
     <string name="button_copy" msgid="8706475544635021302">"Kopijuoti"</string>
     <string name="button_move" msgid="2202666023104202232">"Perkelti"</string>
     <string name="button_dismiss" msgid="3714065566893946085">"Atsisakyti"</string>
-    <!-- no translation found for button_retry (4392027584153752797) -->
-    <skip />
+    <string name="button_retry" msgid="4392027584153752797">"Bandyti dar kartą"</string>
     <string name="sort_name" msgid="9183560467917256779">"Pagal pavadinimą"</string>
     <string name="sort_date" msgid="586080032956151448">"Pagal keitimo datą"</string>
     <string name="sort_size" msgid="3350681319735474741">"Pagal dydį"</string>
diff --git a/packages/DocumentsUI/res/values-lv/strings.xml b/packages/DocumentsUI/res/values-lv/strings.xml
index 5c79554..48715e6 100644
--- a/packages/DocumentsUI/res/values-lv/strings.xml
+++ b/packages/DocumentsUI/res/values-lv/strings.xml
@@ -20,7 +20,7 @@
     <string name="files_label" msgid="6051402950202690279">"Faili"</string>
     <string name="title_open" msgid="4353228937663917801">"Atvēršana no:"</string>
     <string name="title_save" msgid="2433679664882857999">"Saglabāšana:"</string>
-    <string name="menu_create_dir" msgid="5947289605844398389">"Izveidot mapi"</string>
+    <string name="menu_create_dir" msgid="2547620241173881754">"Jauna mape"</string>
     <string name="menu_grid" msgid="6878021334497835259">"Režģis"</string>
     <string name="menu_list" msgid="7279285939892417279">"Saraksts"</string>
     <string name="menu_sort" msgid="7677740407158414452">"Kārtot pēc"</string>
@@ -33,6 +33,7 @@
     <string name="menu_select_all" msgid="8323579667348729928">"Atlasīt visus"</string>
     <string name="menu_copy" msgid="3612326052677229148">"Kopēt…"</string>
     <string name="menu_move" msgid="1828090633118079817">"Pārvietot uz…"</string>
+    <string name="menu_new_window" msgid="1226032889278727538">"Jauns logs"</string>
     <string name="menu_copy_to_clipboard" msgid="489311381979634291">"Kopēt"</string>
     <string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Ielīmēt"</string>
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Rādīt iekšējo atmiņu"</string>
@@ -45,8 +46,7 @@
     <string name="button_copy" msgid="8706475544635021302">"Kopēt"</string>
     <string name="button_move" msgid="2202666023104202232">"Pārvietot"</string>
     <string name="button_dismiss" msgid="3714065566893946085">"Noraidīt"</string>
-    <!-- no translation found for button_retry (4392027584153752797) -->
-    <skip />
+    <string name="button_retry" msgid="4392027584153752797">"Mēģināt vēlreiz"</string>
     <string name="sort_name" msgid="9183560467917256779">"Pēc nosaukuma"</string>
     <string name="sort_date" msgid="586080032956151448">"Pēc pārveidošanas datuma"</string>
     <string name="sort_size" msgid="3350681319735474741">"Pēc lieluma"</string>
diff --git a/packages/DocumentsUI/res/values-mk-rMK/strings.xml b/packages/DocumentsUI/res/values-mk-rMK/strings.xml
index b2cb9f1..950a9b1 100644
--- a/packages/DocumentsUI/res/values-mk-rMK/strings.xml
+++ b/packages/DocumentsUI/res/values-mk-rMK/strings.xml
@@ -20,7 +20,7 @@
     <string name="files_label" msgid="6051402950202690279">"Датотеки"</string>
     <string name="title_open" msgid="4353228937663917801">"Отвори од"</string>
     <string name="title_save" msgid="2433679664882857999">"Зачувај во"</string>
-    <string name="menu_create_dir" msgid="5947289605844398389">"Создади папка"</string>
+    <string name="menu_create_dir" msgid="2547620241173881754">"Нова папка"</string>
     <string name="menu_grid" msgid="6878021334497835259">"Приказ на мрежа"</string>
     <string name="menu_list" msgid="7279285939892417279">"Приказ на список"</string>
     <string name="menu_sort" msgid="7677740407158414452">"Подреди по"</string>
@@ -33,6 +33,7 @@
     <string name="menu_select_all" msgid="8323579667348729928">"Избери ги сите"</string>
     <string name="menu_copy" msgid="3612326052677229148">"Копирај во…"</string>
     <string name="menu_move" msgid="1828090633118079817">"Премести во..."</string>
+    <string name="menu_new_window" msgid="1226032889278727538">"Нов прозорец"</string>
     <string name="menu_copy_to_clipboard" msgid="489311381979634291">"Копирај"</string>
     <string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Залепи"</string>
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Прикажи внатрешна мемор."</string>
@@ -45,8 +46,7 @@
     <string name="button_copy" msgid="8706475544635021302">"Копирај"</string>
     <string name="button_move" msgid="2202666023104202232">"Премести"</string>
     <string name="button_dismiss" msgid="3714065566893946085">"Отфрли"</string>
-    <!-- no translation found for button_retry (4392027584153752797) -->
-    <skip />
+    <string name="button_retry" msgid="4392027584153752797">"Обиди се пак"</string>
     <string name="sort_name" msgid="9183560467917256779">"По име"</string>
     <string name="sort_date" msgid="586080032956151448">"Изменети по датум"</string>
     <string name="sort_size" msgid="3350681319735474741">"По големина"</string>
diff --git a/packages/DocumentsUI/res/values-ml-rIN/strings.xml b/packages/DocumentsUI/res/values-ml-rIN/strings.xml
index 07efa66..32411b2 100644
--- a/packages/DocumentsUI/res/values-ml-rIN/strings.xml
+++ b/packages/DocumentsUI/res/values-ml-rIN/strings.xml
@@ -20,7 +20,7 @@
     <string name="files_label" msgid="6051402950202690279">"ഫയലുകൾ"</string>
     <string name="title_open" msgid="4353228937663917801">"ഇതിൽ നിന്നും തുറക്കുക"</string>
     <string name="title_save" msgid="2433679664882857999">"ഇതില്‍‌ സംരക്ഷിക്കുക"</string>
-    <string name="menu_create_dir" msgid="5947289605844398389">"ഫോൾഡർ സൃഷ്‌ടിക്കുക"</string>
+    <string name="menu_create_dir" msgid="2547620241173881754">"പുതിയ ഫോൾഡർ"</string>
     <string name="menu_grid" msgid="6878021334497835259">"ഗ്രിഡ് കാഴ്‌ച"</string>
     <string name="menu_list" msgid="7279285939892417279">"ലിസ്റ്റ് കാഴ്‌ച"</string>
     <string name="menu_sort" msgid="7677740407158414452">"ഇപ്രകാരം അടുക്കുക"</string>
@@ -33,6 +33,7 @@
     <string name="menu_select_all" msgid="8323579667348729928">"എല്ലാം തിരഞ്ഞെടുക്കുക"</string>
     <string name="menu_copy" msgid="3612326052677229148">"ഇതിൽ പകർത്തുക…"</string>
     <string name="menu_move" msgid="1828090633118079817">"ഇതിലേക്ക് നീക്കുക..."</string>
+    <string name="menu_new_window" msgid="1226032889278727538">"പുതിയ വിന്‍‍ഡോ"</string>
     <string name="menu_copy_to_clipboard" msgid="489311381979634291">"പകര്‍ത്തുക"</string>
     <string name="menu_paste_from_clipboard" msgid="2071583031180257091">"ഒട്ടിക്കുക"</string>
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"ആന്തരിക സ്റ്റോറേജ്  കാണിക്കുക"</string>
@@ -45,8 +46,7 @@
     <string name="button_copy" msgid="8706475544635021302">"പകര്‍ത്തുക"</string>
     <string name="button_move" msgid="2202666023104202232">"നീക്കുക"</string>
     <string name="button_dismiss" msgid="3714065566893946085">"ഡിസ്മിസ് ചെയ്യുക"</string>
-    <!-- no translation found for button_retry (4392027584153752797) -->
-    <skip />
+    <string name="button_retry" msgid="4392027584153752797">"വീണ്ടും ശ്രമിക്കുക"</string>
     <string name="sort_name" msgid="9183560467917256779">"പേര് പ്രകാരം"</string>
     <string name="sort_date" msgid="586080032956151448">"പരിഷ്‌ക്കരിച്ച തീയതി പ്രകാരം"</string>
     <string name="sort_size" msgid="3350681319735474741">"വലുപ്പം പ്രകാരം"</string>
diff --git a/packages/DocumentsUI/res/values-mn-rMN/strings.xml b/packages/DocumentsUI/res/values-mn-rMN/strings.xml
index d45778c..0c5c72d 100644
--- a/packages/DocumentsUI/res/values-mn-rMN/strings.xml
+++ b/packages/DocumentsUI/res/values-mn-rMN/strings.xml
@@ -20,7 +20,7 @@
     <string name="files_label" msgid="6051402950202690279">"Файл"</string>
     <string name="title_open" msgid="4353228937663917801">"Нээх"</string>
     <string name="title_save" msgid="2433679664882857999">"Хадгалах"</string>
-    <string name="menu_create_dir" msgid="5947289605844398389">"Фолдер үүсгэх"</string>
+    <string name="menu_create_dir" msgid="2547620241173881754">"Шинэ фолдер"</string>
     <string name="menu_grid" msgid="6878021334497835259">"Эгнүүлж харах"</string>
     <string name="menu_list" msgid="7279285939892417279">"Жагсааж харах"</string>
     <string name="menu_sort" msgid="7677740407158414452">"Эрэмбэлэх"</string>
@@ -33,6 +33,7 @@
     <string name="menu_select_all" msgid="8323579667348729928">"Бүгдийг сонгох"</string>
     <string name="menu_copy" msgid="3612326052677229148">"...руу хуулах"</string>
     <string name="menu_move" msgid="1828090633118079817">"Байршуулах газар"</string>
+    <string name="menu_new_window" msgid="1226032889278727538">"Шинэ цонх"</string>
     <string name="menu_copy_to_clipboard" msgid="489311381979634291">"Хуулах"</string>
     <string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Буулгах"</string>
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Дотоод санг харуулах"</string>
@@ -45,8 +46,7 @@
     <string name="button_copy" msgid="8706475544635021302">"Хуулах"</string>
     <string name="button_move" msgid="2202666023104202232">"Зөөх"</string>
     <string name="button_dismiss" msgid="3714065566893946085">"Алгасах"</string>
-    <!-- no translation found for button_retry (4392027584153752797) -->
-    <skip />
+    <string name="button_retry" msgid="4392027584153752797">"Дахин оролдоно уу"</string>
     <string name="sort_name" msgid="9183560467917256779">"Нэрээр"</string>
     <string name="sort_date" msgid="586080032956151448">"Өөрчлөгдсөн огноогоор"</string>
     <string name="sort_size" msgid="3350681319735474741">"Хэмжээгээр"</string>
diff --git a/packages/DocumentsUI/res/values-mr-rIN/strings.xml b/packages/DocumentsUI/res/values-mr-rIN/strings.xml
index 8d70143..15c1291 100644
--- a/packages/DocumentsUI/res/values-mr-rIN/strings.xml
+++ b/packages/DocumentsUI/res/values-mr-rIN/strings.xml
@@ -20,7 +20,7 @@
     <string name="files_label" msgid="6051402950202690279">"फायली"</string>
     <string name="title_open" msgid="4353228937663917801">"वरून उघडा"</string>
     <string name="title_save" msgid="2433679664882857999">"येथे जतन करा"</string>
-    <string name="menu_create_dir" msgid="5947289605844398389">"फोल्डर तयार करा"</string>
+    <string name="menu_create_dir" msgid="2547620241173881754">"नवीन फोल्डर"</string>
     <string name="menu_grid" msgid="6878021334497835259">"ग्रिड दृश्य"</string>
     <string name="menu_list" msgid="7279285939892417279">"सूची"</string>
     <string name="menu_sort" msgid="7677740407158414452">"नुसार क्रमवारी लावा"</string>
@@ -33,6 +33,7 @@
     <string name="menu_select_all" msgid="8323579667348729928">"सर्व निवडा"</string>
     <string name="menu_copy" msgid="3612326052677229148">"यावर कॉपी करा…"</string>
     <string name="menu_move" msgid="1828090633118079817">"यावर हलवा…"</string>
+    <string name="menu_new_window" msgid="1226032889278727538">"नवीन विंडो"</string>
     <string name="menu_copy_to_clipboard" msgid="489311381979634291">"कॉपी करा"</string>
     <string name="menu_paste_from_clipboard" msgid="2071583031180257091">"पेस्ट करा"</string>
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"अंतर्गत संचयन दर्शवा"</string>
diff --git a/packages/DocumentsUI/res/values-ms-rMY/strings.xml b/packages/DocumentsUI/res/values-ms-rMY/strings.xml
index 2250109..12cf2e3 100644
--- a/packages/DocumentsUI/res/values-ms-rMY/strings.xml
+++ b/packages/DocumentsUI/res/values-ms-rMY/strings.xml
@@ -20,7 +20,7 @@
     <string name="files_label" msgid="6051402950202690279">"Fail"</string>
     <string name="title_open" msgid="4353228937663917801">"Buka dari"</string>
     <string name="title_save" msgid="2433679664882857999">"Simpan ke"</string>
-    <string name="menu_create_dir" msgid="5947289605844398389">"Buat folder"</string>
+    <string name="menu_create_dir" msgid="2547620241173881754">"Folder baharu"</string>
     <string name="menu_grid" msgid="6878021334497835259">"Paparan grid"</string>
     <string name="menu_list" msgid="7279285939892417279">"Paparan senarai"</string>
     <string name="menu_sort" msgid="7677740407158414452">"Isih mengikut"</string>
@@ -33,6 +33,7 @@
     <string name="menu_select_all" msgid="8323579667348729928">"Pilih semua"</string>
     <string name="menu_copy" msgid="3612326052677229148">"Salin ke..."</string>
     <string name="menu_move" msgid="1828090633118079817">"Alihkan ke…"</string>
+    <string name="menu_new_window" msgid="1226032889278727538">"Tetingkap baharu"</string>
     <string name="menu_copy_to_clipboard" msgid="489311381979634291">"Salin"</string>
     <string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Tampal"</string>
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Papar storan dalaman"</string>
@@ -45,8 +46,7 @@
     <string name="button_copy" msgid="8706475544635021302">"Salin"</string>
     <string name="button_move" msgid="2202666023104202232">"Alihkan"</string>
     <string name="button_dismiss" msgid="3714065566893946085">"Tolak"</string>
-    <!-- no translation found for button_retry (4392027584153752797) -->
-    <skip />
+    <string name="button_retry" msgid="4392027584153752797">"Cuba Lagi"</string>
     <string name="sort_name" msgid="9183560467917256779">"Mengikut nama"</string>
     <string name="sort_date" msgid="586080032956151448">"Mengikut tarikh diubah"</string>
     <string name="sort_size" msgid="3350681319735474741">"Mengikut saiz"</string>
diff --git a/packages/DocumentsUI/res/values-my-rMM/strings.xml b/packages/DocumentsUI/res/values-my-rMM/strings.xml
index 480c27b..a1ab012 100644
--- a/packages/DocumentsUI/res/values-my-rMM/strings.xml
+++ b/packages/DocumentsUI/res/values-my-rMM/strings.xml
@@ -20,7 +20,7 @@
     <string name="files_label" msgid="6051402950202690279">"ဖိုင်များ"</string>
     <string name="title_open" msgid="4353228937663917801">"မှ ဖွင့်ပါ"</string>
     <string name="title_save" msgid="2433679664882857999">"သို့ သိမ်းပါ"</string>
-    <string name="menu_create_dir" msgid="5947289605844398389">"အကန့် တည်ဆောက်ရန်"</string>
+    <string name="menu_create_dir" msgid="2547620241173881754">"ဖိုလ်ဒါ အသစ်"</string>
     <string name="menu_grid" msgid="6878021334497835259">"ဖယားကွက်မြင်ကွင်း"</string>
     <string name="menu_list" msgid="7279285939892417279">"အစဉ်လိုက်မြင်ကွင်း"</string>
     <string name="menu_sort" msgid="7677740407158414452">"အစဉ်အလိုက် စီခြင်း"</string>
@@ -33,6 +33,7 @@
     <string name="menu_select_all" msgid="8323579667348729928">"အားလုံးကို ရွေးရန်"</string>
     <string name="menu_copy" msgid="3612326052677229148">"သို့ကူးယူရန်…"</string>
     <string name="menu_move" msgid="1828090633118079817">"...သို့ ရွှေ့ရန်"</string>
+    <string name="menu_new_window" msgid="1226032889278727538">"ဝင်းဒိုးသစ်"</string>
     <string name="menu_copy_to_clipboard" msgid="489311381979634291">"ကူးယူရန်"</string>
     <string name="menu_paste_from_clipboard" msgid="2071583031180257091">"ကပ်ရန်"</string>
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"စက်ရှိစတိုရုံ ပြပါ"</string>
diff --git a/packages/DocumentsUI/res/values-nb/strings.xml b/packages/DocumentsUI/res/values-nb/strings.xml
index 48355aa..edeaa6d 100644
--- a/packages/DocumentsUI/res/values-nb/strings.xml
+++ b/packages/DocumentsUI/res/values-nb/strings.xml
@@ -20,7 +20,7 @@
     <string name="files_label" msgid="6051402950202690279">"Filer"</string>
     <string name="title_open" msgid="4353228937663917801">"Åpne fra"</string>
     <string name="title_save" msgid="2433679664882857999">"Lagre i"</string>
-    <string name="menu_create_dir" msgid="5947289605844398389">"Opprett en mappe"</string>
+    <string name="menu_create_dir" msgid="2547620241173881754">"Ny mappe"</string>
     <string name="menu_grid" msgid="6878021334497835259">"Rutenettvisning"</string>
     <string name="menu_list" msgid="7279285939892417279">"Listevisning"</string>
     <string name="menu_sort" msgid="7677740407158414452">"Sortér etter"</string>
@@ -33,6 +33,7 @@
     <string name="menu_select_all" msgid="8323579667348729928">"Markér alt"</string>
     <string name="menu_copy" msgid="3612326052677229148">"Kopiér til …"</string>
     <string name="menu_move" msgid="1828090633118079817">"Flytt til"</string>
+    <string name="menu_new_window" msgid="1226032889278727538">"Nytt vindu"</string>
     <string name="menu_copy_to_clipboard" msgid="489311381979634291">"Kopiér"</string>
     <string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Lim inn"</string>
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Vis den interne lagringen"</string>
diff --git a/packages/DocumentsUI/res/values-ne-rNP/strings.xml b/packages/DocumentsUI/res/values-ne-rNP/strings.xml
index 53942c1..bd54211 100644
--- a/packages/DocumentsUI/res/values-ne-rNP/strings.xml
+++ b/packages/DocumentsUI/res/values-ne-rNP/strings.xml
@@ -20,7 +20,7 @@
     <string name="files_label" msgid="6051402950202690279">"फाइलहरू"</string>
     <string name="title_open" msgid="4353228937663917801">"यसबाट खोल्नुहोस्"</string>
     <string name="title_save" msgid="2433679664882857999">"यसमा सुरक्षित गर्नुहोस्"</string>
-    <string name="menu_create_dir" msgid="5947289605844398389">"फोल्डर सिर्जना गर्नुहोस्"</string>
+    <string name="menu_create_dir" msgid="2547620241173881754">"नयाँ फोल्डर"</string>
     <string name="menu_grid" msgid="6878021334497835259">"ग्रिड दृश्य"</string>
     <string name="menu_list" msgid="7279285939892417279">"सूची दृश्य"</string>
     <string name="menu_sort" msgid="7677740407158414452">"यसद्वारा क्रमवद्घ गर्नुहोस्"</string>
@@ -33,6 +33,7 @@
     <string name="menu_select_all" msgid="8323579667348729928">"सबै चयन गर्नुहोस्"</string>
     <string name="menu_copy" msgid="3612326052677229148">"यसमा प्रतिलिपि गर्नुहोस् ..."</string>
     <string name="menu_move" msgid="1828090633118079817">"…मा सार्नुहोस्"</string>
+    <string name="menu_new_window" msgid="1226032889278727538">"नयाँ विन्डो"</string>
     <string name="menu_copy_to_clipboard" msgid="489311381979634291">"प्रतिलिपि बनाउनुहोस्"</string>
     <string name="menu_paste_from_clipboard" msgid="2071583031180257091">"टाँस्नुहोस्"</string>
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"आन्तरिक भण्डारण देखाउनुहोस्"</string>
@@ -45,8 +46,7 @@
     <string name="button_copy" msgid="8706475544635021302">"प्रतिलिपि बनाउनुहोस्"</string>
     <string name="button_move" msgid="2202666023104202232">"सार्नुहोस्"</string>
     <string name="button_dismiss" msgid="3714065566893946085">"खारेज गर्नुहोस्"</string>
-    <!-- no translation found for button_retry (4392027584153752797) -->
-    <skip />
+    <string name="button_retry" msgid="4392027584153752797">"पुन: प्रयास गर्नुहोस्"</string>
     <string name="sort_name" msgid="9183560467917256779">"नाम अनुसार"</string>
     <string name="sort_date" msgid="586080032956151448">"परिमार्जित मिति अनुसार"</string>
     <string name="sort_size" msgid="3350681319735474741">"आकार अनुसार"</string>
diff --git a/packages/DocumentsUI/res/values-nl/strings.xml b/packages/DocumentsUI/res/values-nl/strings.xml
index d3e6bbe..f2eda72 100644
--- a/packages/DocumentsUI/res/values-nl/strings.xml
+++ b/packages/DocumentsUI/res/values-nl/strings.xml
@@ -20,7 +20,7 @@
     <string name="files_label" msgid="6051402950202690279">"Bestanden"</string>
     <string name="title_open" msgid="4353228937663917801">"Openen vanuit"</string>
     <string name="title_save" msgid="2433679664882857999">"Opslaan in"</string>
-    <string name="menu_create_dir" msgid="5947289605844398389">"Map maken"</string>
+    <string name="menu_create_dir" msgid="2547620241173881754">"Nieuwe map"</string>
     <string name="menu_grid" msgid="6878021334497835259">"Rasterweergave"</string>
     <string name="menu_list" msgid="7279285939892417279">"Lijstweergave"</string>
     <string name="menu_sort" msgid="7677740407158414452">"Sorteren op"</string>
@@ -33,6 +33,7 @@
     <string name="menu_select_all" msgid="8323579667348729928">"Alles selecteren"</string>
     <string name="menu_copy" msgid="3612326052677229148">"Kopiëren naar…"</string>
     <string name="menu_move" msgid="1828090633118079817">"Verplaatsen naar…"</string>
+    <string name="menu_new_window" msgid="1226032889278727538">"Nieuw venster"</string>
     <string name="menu_copy_to_clipboard" msgid="489311381979634291">"Kopiëren"</string>
     <string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Plakken"</string>
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Interne opslag weergeven"</string>
diff --git a/packages/DocumentsUI/res/values-pa-rIN/strings.xml b/packages/DocumentsUI/res/values-pa-rIN/strings.xml
index 3b14396..e139dfd 100644
--- a/packages/DocumentsUI/res/values-pa-rIN/strings.xml
+++ b/packages/DocumentsUI/res/values-pa-rIN/strings.xml
@@ -20,7 +20,7 @@
     <string name="files_label" msgid="6051402950202690279">"ਫਾਈਲਾਂ"</string>
     <string name="title_open" msgid="4353228937663917801">"ਤੋਂ ਖੋਲ੍ਹੋ"</string>
     <string name="title_save" msgid="2433679664882857999">"ਇਸ ਵਿੱਚ ਸੁਰੱਖਿਅਤ ਕਰੋ"</string>
-    <string name="menu_create_dir" msgid="5947289605844398389">"ਫੋਲਡਰ ਬਣਾਓ"</string>
+    <string name="menu_create_dir" msgid="2547620241173881754">"ਨਵਾਂ ਫੋਲਡਰ"</string>
     <string name="menu_grid" msgid="6878021334497835259">"ਗ੍ਰਿਡ ਵਿਊ"</string>
     <string name="menu_list" msgid="7279285939892417279">"ਸੂਚੀ ਦ੍ਰਿਸ਼"</string>
     <string name="menu_sort" msgid="7677740407158414452">"ਇਸ ਅਨੁਸਾਰ ਛਾਂਟੋ"</string>
@@ -33,6 +33,7 @@
     <string name="menu_select_all" msgid="8323579667348729928">"ਸਾਰੇ ਚੁਣੋ"</string>
     <string name="menu_copy" msgid="3612326052677229148">"ਇਸ ਵਿੱਚ ਕਾਪੀ ਕਰੋ…"</string>
     <string name="menu_move" msgid="1828090633118079817">"ਇਸ ਵਿੱਚ ਮੂਵ ਕਰੋ..."</string>
+    <string name="menu_new_window" msgid="1226032889278727538">"ਨਵੀਂ ਵਿੰਡੋ"</string>
     <string name="menu_copy_to_clipboard" msgid="489311381979634291">"ਕਾਪੀ ਕਰੋ"</string>
     <string name="menu_paste_from_clipboard" msgid="2071583031180257091">"ਪੇਸਟ ਕਰੋ"</string>
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"ਅੰਦਰੂਨੀ ਸਟੋਰੇਜ ਦਿਖਾਓ"</string>
@@ -45,8 +46,7 @@
     <string name="button_copy" msgid="8706475544635021302">"ਕਾਪੀ ਕਰੋ"</string>
     <string name="button_move" msgid="2202666023104202232">"ਮੂਵ ਕਰੋ"</string>
     <string name="button_dismiss" msgid="3714065566893946085">"ਬਰਖਾਸਤ ਕਰੋ"</string>
-    <!-- no translation found for button_retry (4392027584153752797) -->
-    <skip />
+    <string name="button_retry" msgid="4392027584153752797">"ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ"</string>
     <string name="sort_name" msgid="9183560467917256779">"ਨਾਮ ਮੁਤਾਬਕ"</string>
     <string name="sort_date" msgid="586080032956151448">"ਤਾਰੀਖ ਮੁਤਾਬਕ ਸੰਸ਼ੋਧਿਤ"</string>
     <string name="sort_size" msgid="3350681319735474741">"ਆਕਾਰ ਮੁਤਾਬਕ"</string>
diff --git a/packages/DocumentsUI/res/values-pl/strings.xml b/packages/DocumentsUI/res/values-pl/strings.xml
index 3e4ef68..bb81377 100644
--- a/packages/DocumentsUI/res/values-pl/strings.xml
+++ b/packages/DocumentsUI/res/values-pl/strings.xml
@@ -20,7 +20,7 @@
     <string name="files_label" msgid="6051402950202690279">"Pliki"</string>
     <string name="title_open" msgid="4353228937663917801">"Otwórz z"</string>
     <string name="title_save" msgid="2433679664882857999">"Zapisz w"</string>
-    <string name="menu_create_dir" msgid="5947289605844398389">"Utwórz folder"</string>
+    <string name="menu_create_dir" msgid="2547620241173881754">"Nowy folder"</string>
     <string name="menu_grid" msgid="6878021334497835259">"Widok siatki"</string>
     <string name="menu_list" msgid="7279285939892417279">"Widok listy"</string>
     <string name="menu_sort" msgid="7677740407158414452">"Sortuj według"</string>
@@ -33,6 +33,7 @@
     <string name="menu_select_all" msgid="8323579667348729928">"Zaznacz wszystko"</string>
     <string name="menu_copy" msgid="3612326052677229148">"Kopiuj do…"</string>
     <string name="menu_move" msgid="1828090633118079817">"Przenieś do…"</string>
+    <string name="menu_new_window" msgid="1226032889278727538">"Nowe okno"</string>
     <string name="menu_copy_to_clipboard" msgid="489311381979634291">"Kopiuj"</string>
     <string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Wklej"</string>
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Pokaż pamięć wewnętrzną"</string>
@@ -45,8 +46,7 @@
     <string name="button_copy" msgid="8706475544635021302">"Kopiuj"</string>
     <string name="button_move" msgid="2202666023104202232">"Przenieś"</string>
     <string name="button_dismiss" msgid="3714065566893946085">"Zamknij"</string>
-    <!-- no translation found for button_retry (4392027584153752797) -->
-    <skip />
+    <string name="button_retry" msgid="4392027584153752797">"Spróbuj ponownie"</string>
     <string name="sort_name" msgid="9183560467917256779">"Według nazwy"</string>
     <string name="sort_date" msgid="586080032956151448">"Według daty edycji"</string>
     <string name="sort_size" msgid="3350681319735474741">"Według rozmiaru"</string>
diff --git a/packages/DocumentsUI/res/values-pt-rBR/strings.xml b/packages/DocumentsUI/res/values-pt-rBR/strings.xml
index ca984cf..d72f0c3 100644
--- a/packages/DocumentsUI/res/values-pt-rBR/strings.xml
+++ b/packages/DocumentsUI/res/values-pt-rBR/strings.xml
@@ -20,7 +20,7 @@
     <string name="files_label" msgid="6051402950202690279">"Arquivos"</string>
     <string name="title_open" msgid="4353228937663917801">"Abrir de"</string>
     <string name="title_save" msgid="2433679664882857999">"Salvar em"</string>
-    <string name="menu_create_dir" msgid="5947289605844398389">"Criar pasta"</string>
+    <string name="menu_create_dir" msgid="2547620241173881754">"Nova pasta"</string>
     <string name="menu_grid" msgid="6878021334497835259">"Visualização em grade"</string>
     <string name="menu_list" msgid="7279285939892417279">"Visualização em lista"</string>
     <string name="menu_sort" msgid="7677740407158414452">"Classificar por"</string>
@@ -33,6 +33,7 @@
     <string name="menu_select_all" msgid="8323579667348729928">"Selecionar tudo"</string>
     <string name="menu_copy" msgid="3612326052677229148">"Copiar para..."</string>
     <string name="menu_move" msgid="1828090633118079817">"Mover para..."</string>
+    <string name="menu_new_window" msgid="1226032889278727538">"Nova janela"</string>
     <string name="menu_copy_to_clipboard" msgid="489311381979634291">"Copiar"</string>
     <string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Colar"</string>
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Mostrar armaz. interno"</string>
diff --git a/packages/DocumentsUI/res/values-pt-rPT/strings.xml b/packages/DocumentsUI/res/values-pt-rPT/strings.xml
index ab67358..43265a5 100644
--- a/packages/DocumentsUI/res/values-pt-rPT/strings.xml
+++ b/packages/DocumentsUI/res/values-pt-rPT/strings.xml
@@ -20,7 +20,7 @@
     <string name="files_label" msgid="6051402950202690279">"Ficheiros"</string>
     <string name="title_open" msgid="4353228937663917801">"Abrir de"</string>
     <string name="title_save" msgid="2433679664882857999">"Guardar em"</string>
-    <string name="menu_create_dir" msgid="5947289605844398389">"Criar pasta"</string>
+    <string name="menu_create_dir" msgid="2547620241173881754">"Nova pasta"</string>
     <string name="menu_grid" msgid="6878021334497835259">"Vista de grelha"</string>
     <string name="menu_list" msgid="7279285939892417279">"Vista de lista"</string>
     <string name="menu_sort" msgid="7677740407158414452">"Ordenar por"</string>
@@ -33,6 +33,7 @@
     <string name="menu_select_all" msgid="8323579667348729928">"Selecionar tudo"</string>
     <string name="menu_copy" msgid="3612326052677229148">"Copiar para…"</string>
     <string name="menu_move" msgid="1828090633118079817">"Mover para..."</string>
+    <string name="menu_new_window" msgid="1226032889278727538">"Nova janela"</string>
     <string name="menu_copy_to_clipboard" msgid="489311381979634291">"Copiar"</string>
     <string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Colar"</string>
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Mostrar mem. armaz. int."</string>
diff --git a/packages/DocumentsUI/res/values-pt/strings.xml b/packages/DocumentsUI/res/values-pt/strings.xml
index ca984cf..d72f0c3 100644
--- a/packages/DocumentsUI/res/values-pt/strings.xml
+++ b/packages/DocumentsUI/res/values-pt/strings.xml
@@ -20,7 +20,7 @@
     <string name="files_label" msgid="6051402950202690279">"Arquivos"</string>
     <string name="title_open" msgid="4353228937663917801">"Abrir de"</string>
     <string name="title_save" msgid="2433679664882857999">"Salvar em"</string>
-    <string name="menu_create_dir" msgid="5947289605844398389">"Criar pasta"</string>
+    <string name="menu_create_dir" msgid="2547620241173881754">"Nova pasta"</string>
     <string name="menu_grid" msgid="6878021334497835259">"Visualização em grade"</string>
     <string name="menu_list" msgid="7279285939892417279">"Visualização em lista"</string>
     <string name="menu_sort" msgid="7677740407158414452">"Classificar por"</string>
@@ -33,6 +33,7 @@
     <string name="menu_select_all" msgid="8323579667348729928">"Selecionar tudo"</string>
     <string name="menu_copy" msgid="3612326052677229148">"Copiar para..."</string>
     <string name="menu_move" msgid="1828090633118079817">"Mover para..."</string>
+    <string name="menu_new_window" msgid="1226032889278727538">"Nova janela"</string>
     <string name="menu_copy_to_clipboard" msgid="489311381979634291">"Copiar"</string>
     <string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Colar"</string>
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Mostrar armaz. interno"</string>
diff --git a/packages/DocumentsUI/res/values-ro/strings.xml b/packages/DocumentsUI/res/values-ro/strings.xml
index e927b78..e880bbd 100644
--- a/packages/DocumentsUI/res/values-ro/strings.xml
+++ b/packages/DocumentsUI/res/values-ro/strings.xml
@@ -20,7 +20,7 @@
     <string name="files_label" msgid="6051402950202690279">"Fișiere"</string>
     <string name="title_open" msgid="4353228937663917801">"Deschideți din"</string>
     <string name="title_save" msgid="2433679664882857999">"Salvați în"</string>
-    <string name="menu_create_dir" msgid="5947289605844398389">"Creați un dosar"</string>
+    <string name="menu_create_dir" msgid="2547620241173881754">"Dosar nou"</string>
     <string name="menu_grid" msgid="6878021334497835259">"Afișare tip grilă"</string>
     <string name="menu_list" msgid="7279285939892417279">"Afișare tip listă"</string>
     <string name="menu_sort" msgid="7677740407158414452">"Sortați după"</string>
@@ -33,6 +33,7 @@
     <string name="menu_select_all" msgid="8323579667348729928">"Selectați tot"</string>
     <string name="menu_copy" msgid="3612326052677229148">"Copiați în…"</string>
     <string name="menu_move" msgid="1828090633118079817">"Mutați în…"</string>
+    <string name="menu_new_window" msgid="1226032889278727538">"Fereastră nouă"</string>
     <string name="menu_copy_to_clipboard" msgid="489311381979634291">"Copiați"</string>
     <string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Inserați"</string>
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Afișați stocarea internă"</string>
diff --git a/packages/DocumentsUI/res/values-ru/strings.xml b/packages/DocumentsUI/res/values-ru/strings.xml
index cdf20ca..e475591 100644
--- a/packages/DocumentsUI/res/values-ru/strings.xml
+++ b/packages/DocumentsUI/res/values-ru/strings.xml
@@ -20,7 +20,7 @@
     <string name="files_label" msgid="6051402950202690279">"Файлы"</string>
     <string name="title_open" msgid="4353228937663917801">"Открыть"</string>
     <string name="title_save" msgid="2433679664882857999">"Сохранить"</string>
-    <string name="menu_create_dir" msgid="5947289605844398389">"Новая папка"</string>
+    <string name="menu_create_dir" msgid="2547620241173881754">"Создать папку"</string>
     <string name="menu_grid" msgid="6878021334497835259">"Сетка"</string>
     <string name="menu_list" msgid="7279285939892417279">"Список"</string>
     <string name="menu_sort" msgid="7677740407158414452">"Сортировать"</string>
@@ -33,6 +33,7 @@
     <string name="menu_select_all" msgid="8323579667348729928">"Выбрать все"</string>
     <string name="menu_copy" msgid="3612326052677229148">"Копировать в…"</string>
     <string name="menu_move" msgid="1828090633118079817">"Переместить"</string>
+    <string name="menu_new_window" msgid="1226032889278727538">"Новое окно"</string>
     <string name="menu_copy_to_clipboard" msgid="489311381979634291">"Копировать"</string>
     <string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Вставить"</string>
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Внутренняя память"</string>
@@ -45,8 +46,7 @@
     <string name="button_copy" msgid="8706475544635021302">"Копировать"</string>
     <string name="button_move" msgid="2202666023104202232">"Переместить"</string>
     <string name="button_dismiss" msgid="3714065566893946085">"Скрыть"</string>
-    <!-- no translation found for button_retry (4392027584153752797) -->
-    <skip />
+    <string name="button_retry" msgid="4392027584153752797">"Повторить"</string>
     <string name="sort_name" msgid="9183560467917256779">"По названию"</string>
     <string name="sort_date" msgid="586080032956151448">"По дате изменения"</string>
     <string name="sort_size" msgid="3350681319735474741">"По размеру"</string>
diff --git a/packages/DocumentsUI/res/values-si-rLK/strings.xml b/packages/DocumentsUI/res/values-si-rLK/strings.xml
index 138f8a6..e380b0a 100644
--- a/packages/DocumentsUI/res/values-si-rLK/strings.xml
+++ b/packages/DocumentsUI/res/values-si-rLK/strings.xml
@@ -20,7 +20,7 @@
     <string name="files_label" msgid="6051402950202690279">"ගොනු"</string>
     <string name="title_open" msgid="4353228937663917801">"විවෘත වන්නේ"</string>
     <string name="title_save" msgid="2433679664882857999">"සුරකින්නේ"</string>
-    <string name="menu_create_dir" msgid="5947289605844398389">"ෆෝල්ඩරයක් සාදන්න"</string>
+    <string name="menu_create_dir" msgid="2547620241173881754">"නව ෆෝල්ඩරය"</string>
     <string name="menu_grid" msgid="6878021334497835259">"ජාල පෙනුම"</string>
     <string name="menu_list" msgid="7279285939892417279">"ලැයිස්තු පෙනුම"</string>
     <string name="menu_sort" msgid="7677740407158414452">"අනුපිළිවෙලට සකසා ඇත්තේ"</string>
@@ -33,6 +33,7 @@
     <string name="menu_select_all" msgid="8323579667348729928">"සියල්ල තෝරන්න"</string>
     <string name="menu_copy" msgid="3612326052677229148">"වෙත පිටපත් කරන්න..."</string>
     <string name="menu_move" msgid="1828090633118079817">"වෙත ගෙනයන්න..."</string>
+    <string name="menu_new_window" msgid="1226032889278727538">"නව කවුළුව"</string>
     <string name="menu_copy_to_clipboard" msgid="489311381979634291">"පිටපත් කරන්න"</string>
     <string name="menu_paste_from_clipboard" msgid="2071583031180257091">"අලවන්න"</string>
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"අභ්‍යන්තර ආචයනය පෙන්වන්න"</string>
@@ -45,8 +46,7 @@
     <string name="button_copy" msgid="8706475544635021302">"පිටපත් කිරීම"</string>
     <string name="button_move" msgid="2202666023104202232">"ගෙන යන්න"</string>
     <string name="button_dismiss" msgid="3714065566893946085">"ඉවතලන්න"</string>
-    <!-- no translation found for button_retry (4392027584153752797) -->
-    <skip />
+    <string name="button_retry" msgid="4392027584153752797">"නැවත උත්සාහ කරන්න"</string>
     <string name="sort_name" msgid="9183560467917256779">"නමින්"</string>
     <string name="sort_date" msgid="586080032956151448">"වෙනස් කරන ලද දිනයෙන්"</string>
     <string name="sort_size" msgid="3350681319735474741">"ප්‍රමාණය මගින්"</string>
diff --git a/packages/DocumentsUI/res/values-sk/strings.xml b/packages/DocumentsUI/res/values-sk/strings.xml
index 4310819..6614dca 100644
--- a/packages/DocumentsUI/res/values-sk/strings.xml
+++ b/packages/DocumentsUI/res/values-sk/strings.xml
@@ -20,7 +20,7 @@
     <string name="files_label" msgid="6051402950202690279">"Súbory"</string>
     <string name="title_open" msgid="4353228937663917801">"Otvoriť z"</string>
     <string name="title_save" msgid="2433679664882857999">"Uložiť do"</string>
-    <string name="menu_create_dir" msgid="5947289605844398389">"Vytvoriť priečinok"</string>
+    <string name="menu_create_dir" msgid="2547620241173881754">"Nový priečinok"</string>
     <string name="menu_grid" msgid="6878021334497835259">"Zobrazenie mriežky"</string>
     <string name="menu_list" msgid="7279285939892417279">"Zobrazenie zoznamu"</string>
     <string name="menu_sort" msgid="7677740407158414452">"Zoradiť podľa"</string>
@@ -33,6 +33,7 @@
     <string name="menu_select_all" msgid="8323579667348729928">"Vybrať všetko"</string>
     <string name="menu_copy" msgid="3612326052677229148">"Kopírovať do…"</string>
     <string name="menu_move" msgid="1828090633118079817">"Presunúť do…"</string>
+    <string name="menu_new_window" msgid="1226032889278727538">"Nové okno"</string>
     <string name="menu_copy_to_clipboard" msgid="489311381979634291">"Kopírovať"</string>
     <string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Prilepiť"</string>
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Zobraziť interné úložisko"</string>
@@ -45,8 +46,7 @@
     <string name="button_copy" msgid="8706475544635021302">"Kopírovať"</string>
     <string name="button_move" msgid="2202666023104202232">"Presunúť"</string>
     <string name="button_dismiss" msgid="3714065566893946085">"Odmietnuť"</string>
-    <!-- no translation found for button_retry (4392027584153752797) -->
-    <skip />
+    <string name="button_retry" msgid="4392027584153752797">"Skúsiť znova"</string>
     <string name="sort_name" msgid="9183560467917256779">"Podľa názvu"</string>
     <string name="sort_date" msgid="586080032956151448">"Podľa dátumu zmeny"</string>
     <string name="sort_size" msgid="3350681319735474741">"Podľa veľkosti"</string>
diff --git a/packages/DocumentsUI/res/values-sl/strings.xml b/packages/DocumentsUI/res/values-sl/strings.xml
index c9982a7..58a4cc0 100644
--- a/packages/DocumentsUI/res/values-sl/strings.xml
+++ b/packages/DocumentsUI/res/values-sl/strings.xml
@@ -20,7 +20,7 @@
     <string name="files_label" msgid="6051402950202690279">"Datoteke"</string>
     <string name="title_open" msgid="4353228937663917801">"Odpri iz mape"</string>
     <string name="title_save" msgid="2433679664882857999">"Shrani v"</string>
-    <string name="menu_create_dir" msgid="5947289605844398389">"Ustvarjanje mape"</string>
+    <string name="menu_create_dir" msgid="2547620241173881754">"Nova mapa"</string>
     <string name="menu_grid" msgid="6878021334497835259">"Mrežni pogled"</string>
     <string name="menu_list" msgid="7279285939892417279">"Pogled seznama"</string>
     <string name="menu_sort" msgid="7677740407158414452">"Razvrsti glede na"</string>
@@ -33,6 +33,7 @@
     <string name="menu_select_all" msgid="8323579667348729928">"Izberi vse"</string>
     <string name="menu_copy" msgid="3612326052677229148">"Kopiraj v …"</string>
     <string name="menu_move" msgid="1828090633118079817">"Premakni v ..."</string>
+    <string name="menu_new_window" msgid="1226032889278727538">"Novo okno"</string>
     <string name="menu_copy_to_clipboard" msgid="489311381979634291">"Kopiraj"</string>
     <string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Prilepi"</string>
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Pokaži notranjo shrambo"</string>
@@ -45,8 +46,7 @@
     <string name="button_copy" msgid="8706475544635021302">"Kopiraj"</string>
     <string name="button_move" msgid="2202666023104202232">"Premik"</string>
     <string name="button_dismiss" msgid="3714065566893946085">"Opusti"</string>
-    <!-- no translation found for button_retry (4392027584153752797) -->
-    <skip />
+    <string name="button_retry" msgid="4392027584153752797">"Poskusite znova"</string>
     <string name="sort_name" msgid="9183560467917256779">"Po imenu"</string>
     <string name="sort_date" msgid="586080032956151448">"Po datumu spremembe"</string>
     <string name="sort_size" msgid="3350681319735474741">"Po velikosti"</string>
diff --git a/packages/DocumentsUI/res/values-sq-rAL/strings.xml b/packages/DocumentsUI/res/values-sq-rAL/strings.xml
index f1ee1bc..7eba92f 100644
--- a/packages/DocumentsUI/res/values-sq-rAL/strings.xml
+++ b/packages/DocumentsUI/res/values-sq-rAL/strings.xml
@@ -20,7 +20,7 @@
     <string name="files_label" msgid="6051402950202690279">"Skedarët"</string>
     <string name="title_open" msgid="4353228937663917801">"Hap nga"</string>
     <string name="title_save" msgid="2433679664882857999">"Ruaje te"</string>
-    <string name="menu_create_dir" msgid="5947289605844398389">"Krijo dosje"</string>
+    <string name="menu_create_dir" msgid="2547620241173881754">"Dosje e re"</string>
     <string name="menu_grid" msgid="6878021334497835259">"Pamje rrjete"</string>
     <string name="menu_list" msgid="7279285939892417279">"Pamje liste"</string>
     <string name="menu_sort" msgid="7677740407158414452">"Rendit sipas"</string>
@@ -33,6 +33,7 @@
     <string name="menu_select_all" msgid="8323579667348729928">"Zgjidhi të gjitha"</string>
     <string name="menu_copy" msgid="3612326052677229148">"Kopjo te..."</string>
     <string name="menu_move" msgid="1828090633118079817">"Zhvendos te..."</string>
+    <string name="menu_new_window" msgid="1226032889278727538">"Dritare e re"</string>
     <string name="menu_copy_to_clipboard" msgid="489311381979634291">"Kopjo"</string>
     <string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Ngjit"</string>
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Trego hapësirën e brendshme ruajtëse"</string>
@@ -45,8 +46,7 @@
     <string name="button_copy" msgid="8706475544635021302">"Kopjo"</string>
     <string name="button_move" msgid="2202666023104202232">"Zhvendos"</string>
     <string name="button_dismiss" msgid="3714065566893946085">"Largoje"</string>
-    <!-- no translation found for button_retry (4392027584153752797) -->
-    <skip />
+    <string name="button_retry" msgid="4392027584153752797">"Provo sërish"</string>
     <string name="sort_name" msgid="9183560467917256779">"Sipas emrit"</string>
     <string name="sort_date" msgid="586080032956151448">"Sipas datës së modifikimit"</string>
     <string name="sort_size" msgid="3350681319735474741">"Sipas madhësisë"</string>
diff --git a/packages/DocumentsUI/res/values-sr/strings.xml b/packages/DocumentsUI/res/values-sr/strings.xml
index c5116c2..0bce687 100644
--- a/packages/DocumentsUI/res/values-sr/strings.xml
+++ b/packages/DocumentsUI/res/values-sr/strings.xml
@@ -20,7 +20,7 @@
     <string name="files_label" msgid="6051402950202690279">"Датотеке"</string>
     <string name="title_open" msgid="4353228937663917801">"Отвори са"</string>
     <string name="title_save" msgid="2433679664882857999">"Сачувај у"</string>
-    <string name="menu_create_dir" msgid="5947289605844398389">"Направи директоријум"</string>
+    <string name="menu_create_dir" msgid="2547620241173881754">"Нови директоријум"</string>
     <string name="menu_grid" msgid="6878021334497835259">"Приказ мреже"</string>
     <string name="menu_list" msgid="7279285939892417279">"Приказ листе"</string>
     <string name="menu_sort" msgid="7677740407158414452">"Сортирај према"</string>
@@ -33,6 +33,7 @@
     <string name="menu_select_all" msgid="8323579667348729928">"Изабери све"</string>
     <string name="menu_copy" msgid="3612326052677229148">"Копирај на..."</string>
     <string name="menu_move" msgid="1828090633118079817">"Премести у..."</string>
+    <string name="menu_new_window" msgid="1226032889278727538">"Нови прозор"</string>
     <string name="menu_copy_to_clipboard" msgid="489311381979634291">"Копирај"</string>
     <string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Налепи"</string>
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Прикажи интерну меморију"</string>
@@ -45,8 +46,7 @@
     <string name="button_copy" msgid="8706475544635021302">"Копирај"</string>
     <string name="button_move" msgid="2202666023104202232">"Премести"</string>
     <string name="button_dismiss" msgid="3714065566893946085">"Одбаци"</string>
-    <!-- no translation found for button_retry (4392027584153752797) -->
-    <skip />
+    <string name="button_retry" msgid="4392027584153752797">"Покушај поново"</string>
     <string name="sort_name" msgid="9183560467917256779">"Према имену"</string>
     <string name="sort_date" msgid="586080032956151448">"Према датуму измене"</string>
     <string name="sort_size" msgid="3350681319735474741">"Према величини"</string>
diff --git a/packages/DocumentsUI/res/values-sv/strings.xml b/packages/DocumentsUI/res/values-sv/strings.xml
index 06e6514..a4a119c 100644
--- a/packages/DocumentsUI/res/values-sv/strings.xml
+++ b/packages/DocumentsUI/res/values-sv/strings.xml
@@ -20,7 +20,7 @@
     <string name="files_label" msgid="6051402950202690279">"Filer"</string>
     <string name="title_open" msgid="4353228937663917801">"Öppna från"</string>
     <string name="title_save" msgid="2433679664882857999">"Spara till"</string>
-    <string name="menu_create_dir" msgid="5947289605844398389">"Skapa mapp"</string>
+    <string name="menu_create_dir" msgid="2547620241173881754">"Ny mapp"</string>
     <string name="menu_grid" msgid="6878021334497835259">"Rutnätsvy"</string>
     <string name="menu_list" msgid="7279285939892417279">"Listvy"</string>
     <string name="menu_sort" msgid="7677740407158414452">"Sortera efter"</string>
@@ -33,6 +33,7 @@
     <string name="menu_select_all" msgid="8323579667348729928">"Markera allt"</string>
     <string name="menu_copy" msgid="3612326052677229148">"Kopiera till …"</string>
     <string name="menu_move" msgid="1828090633118079817">"Flytta till ..."</string>
+    <string name="menu_new_window" msgid="1226032889278727538">"Nytt fönster"</string>
     <string name="menu_copy_to_clipboard" msgid="489311381979634291">"Kopiera"</string>
     <string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Klistra in"</string>
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Visa internminne"</string>
diff --git a/packages/DocumentsUI/res/values-sw/strings.xml b/packages/DocumentsUI/res/values-sw/strings.xml
index 79bae1f..91151fb 100644
--- a/packages/DocumentsUI/res/values-sw/strings.xml
+++ b/packages/DocumentsUI/res/values-sw/strings.xml
@@ -20,7 +20,7 @@
     <string name="files_label" msgid="6051402950202690279">"Faili"</string>
     <string name="title_open" msgid="4353228937663917801">"Fungua kutoka"</string>
     <string name="title_save" msgid="2433679664882857999">"Hifadhi kwenye"</string>
-    <string name="menu_create_dir" msgid="5947289605844398389">"Unda folda"</string>
+    <string name="menu_create_dir" msgid="2547620241173881754">"Folda mpya"</string>
     <string name="menu_grid" msgid="6878021334497835259">"Mwonekano gridi"</string>
     <string name="menu_list" msgid="7279285939892417279">"Mwonekano orodha"</string>
     <string name="menu_sort" msgid="7677740407158414452">"Panga kwa"</string>
@@ -33,6 +33,7 @@
     <string name="menu_select_all" msgid="8323579667348729928">"Chagua zote"</string>
     <string name="menu_copy" msgid="3612326052677229148">"Nakili kwenda..."</string>
     <string name="menu_move" msgid="1828090633118079817">"Hamisha hadi..."</string>
+    <string name="menu_new_window" msgid="1226032889278727538">"Dirisha jipya"</string>
     <string name="menu_copy_to_clipboard" msgid="489311381979634291">"Nakili"</string>
     <string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Bandika"</string>
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Onyesha hifadhi ya ndani"</string>
diff --git a/packages/DocumentsUI/res/values-sw720dp/styles.xml b/packages/DocumentsUI/res/values-sw720dp/styles.xml
index f4bc88e..d415a84 100644
--- a/packages/DocumentsUI/res/values-sw720dp/styles.xml
+++ b/packages/DocumentsUI/res/values-sw720dp/styles.xml
@@ -16,7 +16,7 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android">
 
-    <style name="DocumentsBaseTheme" parent="@*android:style/Theme.Material.DayNight.Dialog">
+    <style name="DocumentsBaseTheme" parent="@style/Theme.AppCompat.Dialog">
         <!-- We do not specify width of window here because the max size of
              floating window specified by windowFixedWidthis is limited. -->
         <item name="*android:windowFixedHeightMajor">80%</item>
diff --git a/packages/DocumentsUI/res/values-ta-rIN/strings.xml b/packages/DocumentsUI/res/values-ta-rIN/strings.xml
index 117aabc..9a667b8 100644
--- a/packages/DocumentsUI/res/values-ta-rIN/strings.xml
+++ b/packages/DocumentsUI/res/values-ta-rIN/strings.xml
@@ -20,7 +20,7 @@
     <string name="files_label" msgid="6051402950202690279">"கோப்புகள்"</string>
     <string name="title_open" msgid="4353228937663917801">"இதில் திற"</string>
     <string name="title_save" msgid="2433679664882857999">"இதில் சேமி"</string>
-    <string name="menu_create_dir" msgid="5947289605844398389">"கோப்புறையை உருவாக்கு"</string>
+    <string name="menu_create_dir" msgid="2547620241173881754">"புதிய கோப்புறை"</string>
     <string name="menu_grid" msgid="6878021334497835259">"கட்டக் காட்சி"</string>
     <string name="menu_list" msgid="7279285939892417279">"பட்டியல்"</string>
     <string name="menu_sort" msgid="7677740407158414452">"இதன்படி வரிசைப்படுத்து"</string>
@@ -33,6 +33,7 @@
     <string name="menu_select_all" msgid="8323579667348729928">"எல்லாவற்றையும் தேர்ந்தெடு"</string>
     <string name="menu_copy" msgid="3612326052677229148">"இங்கு நகலெடு…"</string>
     <string name="menu_move" msgid="1828090633118079817">"இதற்கு நகர்த்து…"</string>
+    <string name="menu_new_window" msgid="1226032889278727538">"புதிய சாளரம்"</string>
     <string name="menu_copy_to_clipboard" msgid="489311381979634291">"நகலெடு"</string>
     <string name="menu_paste_from_clipboard" msgid="2071583031180257091">"ஒட்டு"</string>
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"அகச் சேமிப்பகத்தைக் காட்டு"</string>
diff --git a/packages/DocumentsUI/res/values-te-rIN/strings.xml b/packages/DocumentsUI/res/values-te-rIN/strings.xml
index 21b7f58..91d436a 100644
--- a/packages/DocumentsUI/res/values-te-rIN/strings.xml
+++ b/packages/DocumentsUI/res/values-te-rIN/strings.xml
@@ -20,7 +20,7 @@
     <string name="files_label" msgid="6051402950202690279">"ఫైల్‌లు"</string>
     <string name="title_open" msgid="4353228937663917801">"ఇక్కడి నుండి తెరువు"</string>
     <string name="title_save" msgid="2433679664882857999">"ఇందులో సేవ్ చేయి"</string>
-    <string name="menu_create_dir" msgid="5947289605844398389">"ఫోల్డర్‌ను సృష్టించు"</string>
+    <string name="menu_create_dir" msgid="2547620241173881754">"కొత్త ఫోల్డర్"</string>
     <string name="menu_grid" msgid="6878021334497835259">"గ్రిడ్ వీక్షణ"</string>
     <string name="menu_list" msgid="7279285939892417279">"జాబితా వీక్షణ"</string>
     <string name="menu_sort" msgid="7677740407158414452">"ఇలా క్రమబద్ధీకరించు"</string>
@@ -33,6 +33,7 @@
     <string name="menu_select_all" msgid="8323579667348729928">"అన్నీ ఎంచుకోండి"</string>
     <string name="menu_copy" msgid="3612326052677229148">"ఇక్కడికి కాపీ చేయి…"</string>
     <string name="menu_move" msgid="1828090633118079817">"దీనికి తరలించు..."</string>
+    <string name="menu_new_window" msgid="1226032889278727538">"కొత్త విండో"</string>
     <string name="menu_copy_to_clipboard" msgid="489311381979634291">"కాపీ చేయి"</string>
     <string name="menu_paste_from_clipboard" msgid="2071583031180257091">"అతికించు"</string>
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"అంతర్గత నిల్వను చూపు"</string>
diff --git a/packages/DocumentsUI/res/values-th/strings.xml b/packages/DocumentsUI/res/values-th/strings.xml
index 8a49708..9d77561 100644
--- a/packages/DocumentsUI/res/values-th/strings.xml
+++ b/packages/DocumentsUI/res/values-th/strings.xml
@@ -20,7 +20,7 @@
     <string name="files_label" msgid="6051402950202690279">"ไฟล์"</string>
     <string name="title_open" msgid="4353228937663917801">"เปิดจาก"</string>
     <string name="title_save" msgid="2433679664882857999">"บันทึกไปยัง"</string>
-    <string name="menu_create_dir" msgid="5947289605844398389">"สร้างโฟลเดอร์"</string>
+    <string name="menu_create_dir" msgid="2547620241173881754">"โฟลเดอร์ใหม่"</string>
     <string name="menu_grid" msgid="6878021334497835259">"มุมมองตาราง"</string>
     <string name="menu_list" msgid="7279285939892417279">"มุมมองรายการ"</string>
     <string name="menu_sort" msgid="7677740407158414452">"จัดเรียงตาม"</string>
@@ -33,6 +33,7 @@
     <string name="menu_select_all" msgid="8323579667348729928">"เลือกทั้งหมด"</string>
     <string name="menu_copy" msgid="3612326052677229148">"คัดลอกไปยัง…"</string>
     <string name="menu_move" msgid="1828090633118079817">"ย้ายไปที่…"</string>
+    <string name="menu_new_window" msgid="1226032889278727538">"หน้าต่างใหม่"</string>
     <string name="menu_copy_to_clipboard" msgid="489311381979634291">"คัดลอก"</string>
     <string name="menu_paste_from_clipboard" msgid="2071583031180257091">"วาง"</string>
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"แสดงที่จัดเก็บภายใน"</string>
@@ -45,8 +46,7 @@
     <string name="button_copy" msgid="8706475544635021302">"คัดลอก"</string>
     <string name="button_move" msgid="2202666023104202232">"ย้าย"</string>
     <string name="button_dismiss" msgid="3714065566893946085">"ปิด"</string>
-    <!-- no translation found for button_retry (4392027584153752797) -->
-    <skip />
+    <string name="button_retry" msgid="4392027584153752797">"ลองอีกครั้ง"</string>
     <string name="sort_name" msgid="9183560467917256779">"ตามชื่อ"</string>
     <string name="sort_date" msgid="586080032956151448">"ตามวันที่ที่ปรับเปลี่ยน"</string>
     <string name="sort_size" msgid="3350681319735474741">"ตามขนาด"</string>
diff --git a/packages/DocumentsUI/res/values-tl/strings.xml b/packages/DocumentsUI/res/values-tl/strings.xml
index 5c0dc12..9ae30aa 100644
--- a/packages/DocumentsUI/res/values-tl/strings.xml
+++ b/packages/DocumentsUI/res/values-tl/strings.xml
@@ -20,7 +20,7 @@
     <string name="files_label" msgid="6051402950202690279">"Mga File"</string>
     <string name="title_open" msgid="4353228937663917801">"Buksan mula sa"</string>
     <string name="title_save" msgid="2433679664882857999">"I-save sa"</string>
-    <string name="menu_create_dir" msgid="5947289605844398389">"Gumawa ng folder"</string>
+    <string name="menu_create_dir" msgid="2547620241173881754">"Bagong folder"</string>
     <string name="menu_grid" msgid="6878021334497835259">"View na grid"</string>
     <string name="menu_list" msgid="7279285939892417279">"View na listahan"</string>
     <string name="menu_sort" msgid="7677740407158414452">"Uriin ayon sa"</string>
@@ -33,6 +33,7 @@
     <string name="menu_select_all" msgid="8323579667348729928">"Piliin lahat"</string>
     <string name="menu_copy" msgid="3612326052677229148">"Kopyahin sa..."</string>
     <string name="menu_move" msgid="1828090633118079817">"Ilipat sa…"</string>
+    <string name="menu_new_window" msgid="1226032889278727538">"Bagong window"</string>
     <string name="menu_copy_to_clipboard" msgid="489311381979634291">"Kopyahin"</string>
     <string name="menu_paste_from_clipboard" msgid="2071583031180257091">"I-paste"</string>
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Ipakita internal storage"</string>
@@ -45,8 +46,7 @@
     <string name="button_copy" msgid="8706475544635021302">"Kopyahin"</string>
     <string name="button_move" msgid="2202666023104202232">"Ilipat"</string>
     <string name="button_dismiss" msgid="3714065566893946085">"I-dismiss"</string>
-    <!-- no translation found for button_retry (4392027584153752797) -->
-    <skip />
+    <string name="button_retry" msgid="4392027584153752797">"Subukang Muli"</string>
     <string name="sort_name" msgid="9183560467917256779">"Ayon sa pangalan"</string>
     <string name="sort_date" msgid="586080032956151448">"Ayon sa petsa ng pagbago"</string>
     <string name="sort_size" msgid="3350681319735474741">"Ayon sa laki"</string>
diff --git a/packages/DocumentsUI/res/values-tr/strings.xml b/packages/DocumentsUI/res/values-tr/strings.xml
index 092b38e..3c2e39c9 100644
--- a/packages/DocumentsUI/res/values-tr/strings.xml
+++ b/packages/DocumentsUI/res/values-tr/strings.xml
@@ -20,7 +20,7 @@
     <string name="files_label" msgid="6051402950202690279">"Dosyalar"</string>
     <string name="title_open" msgid="4353228937663917801">"Şuradan aç:"</string>
     <string name="title_save" msgid="2433679664882857999">"Şuraya kaydet:"</string>
-    <string name="menu_create_dir" msgid="5947289605844398389">"Klasör oluştur"</string>
+    <string name="menu_create_dir" msgid="2547620241173881754">"Yeni klasör"</string>
     <string name="menu_grid" msgid="6878021334497835259">"Tablo görünümü"</string>
     <string name="menu_list" msgid="7279285939892417279">"Liste görünümü"</string>
     <string name="menu_sort" msgid="7677740407158414452">"Sıralama ölçütü"</string>
@@ -33,6 +33,7 @@
     <string name="menu_select_all" msgid="8323579667348729928">"Tümünü seç"</string>
     <string name="menu_copy" msgid="3612326052677229148">"Kopyala…"</string>
     <string name="menu_move" msgid="1828090633118079817">"Taşı..."</string>
+    <string name="menu_new_window" msgid="1226032889278727538">"Yeni pencere"</string>
     <string name="menu_copy_to_clipboard" msgid="489311381979634291">"Kopyala"</string>
     <string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Yapıştır"</string>
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Dahili depolamayı göster"</string>
@@ -45,8 +46,7 @@
     <string name="button_copy" msgid="8706475544635021302">"Kopyala"</string>
     <string name="button_move" msgid="2202666023104202232">"Taşı"</string>
     <string name="button_dismiss" msgid="3714065566893946085">"Kapat"</string>
-    <!-- no translation found for button_retry (4392027584153752797) -->
-    <skip />
+    <string name="button_retry" msgid="4392027584153752797">"Tekrar Dene"</string>
     <string name="sort_name" msgid="9183560467917256779">"Ada göre"</string>
     <string name="sort_date" msgid="586080032956151448">"Değişiklik tarihine göre"</string>
     <string name="sort_size" msgid="3350681319735474741">"Boyuta göre"</string>
diff --git a/packages/DocumentsUI/res/values-uk/strings.xml b/packages/DocumentsUI/res/values-uk/strings.xml
index dff37c3..407f1aa 100644
--- a/packages/DocumentsUI/res/values-uk/strings.xml
+++ b/packages/DocumentsUI/res/values-uk/strings.xml
@@ -20,7 +20,7 @@
     <string name="files_label" msgid="6051402950202690279">"Файли"</string>
     <string name="title_open" msgid="4353228937663917801">"Відкрити"</string>
     <string name="title_save" msgid="2433679664882857999">"Зберегти в"</string>
-    <string name="menu_create_dir" msgid="5947289605844398389">"Створити папку"</string>
+    <string name="menu_create_dir" msgid="2547620241173881754">"Нова папка"</string>
     <string name="menu_grid" msgid="6878021334497835259">"Режим таблиці"</string>
     <string name="menu_list" msgid="7279285939892417279">"Режим списку"</string>
     <string name="menu_sort" msgid="7677740407158414452">"Параметри сортування"</string>
@@ -33,6 +33,7 @@
     <string name="menu_select_all" msgid="8323579667348729928">"Вибрати все"</string>
     <string name="menu_copy" msgid="3612326052677229148">"Копіювати в…"</string>
     <string name="menu_move" msgid="1828090633118079817">"Перемістити в…"</string>
+    <string name="menu_new_window" msgid="1226032889278727538">"Нове вікно"</string>
     <string name="menu_copy_to_clipboard" msgid="489311381979634291">"Копіювати"</string>
     <string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Вставити"</string>
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Показати внутр. пам’ять"</string>
diff --git a/packages/DocumentsUI/res/values-ur-rPK/strings.xml b/packages/DocumentsUI/res/values-ur-rPK/strings.xml
index 82822ec..845d2cb 100644
--- a/packages/DocumentsUI/res/values-ur-rPK/strings.xml
+++ b/packages/DocumentsUI/res/values-ur-rPK/strings.xml
@@ -20,7 +20,7 @@
     <string name="files_label" msgid="6051402950202690279">"فائلیں"</string>
     <string name="title_open" msgid="4353228937663917801">"کھولیں از"</string>
     <string name="title_save" msgid="2433679664882857999">"اس میں محفوظ کریں"</string>
-    <string name="menu_create_dir" msgid="5947289605844398389">"فولڈر بنائیں"</string>
+    <string name="menu_create_dir" msgid="2547620241173881754">"نیا فولڈر"</string>
     <string name="menu_grid" msgid="6878021334497835259">"گرڈ منظر"</string>
     <string name="menu_list" msgid="7279285939892417279">"فہرست منظر"</string>
     <string name="menu_sort" msgid="7677740407158414452">"ترتیب دیں بلحاظ"</string>
@@ -33,6 +33,7 @@
     <string name="menu_select_all" msgid="8323579667348729928">"سبھی کو منتخب کریں"</string>
     <string name="menu_copy" msgid="3612326052677229148">"اس میں کاپی کریں…"</string>
     <string name="menu_move" msgid="1828090633118079817">"اس میں منتقل کریں…"</string>
+    <string name="menu_new_window" msgid="1226032889278727538">"نئی ونڈو"</string>
     <string name="menu_copy_to_clipboard" msgid="489311381979634291">"کاپی کریں"</string>
     <string name="menu_paste_from_clipboard" msgid="2071583031180257091">"پیسٹ کریں"</string>
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"داخلی اسٹوریج دکھائیں"</string>
diff --git a/packages/DocumentsUI/res/values-uz-rUZ/strings.xml b/packages/DocumentsUI/res/values-uz-rUZ/strings.xml
index d0cfacc..f3514db 100644
--- a/packages/DocumentsUI/res/values-uz-rUZ/strings.xml
+++ b/packages/DocumentsUI/res/values-uz-rUZ/strings.xml
@@ -20,7 +20,7 @@
     <string name="files_label" msgid="6051402950202690279">"Fayllar"</string>
     <string name="title_open" msgid="4353228937663917801">"Ochish"</string>
     <string name="title_save" msgid="2433679664882857999">"Saqlash"</string>
-    <string name="menu_create_dir" msgid="5947289605844398389">"Jild yaratish"</string>
+    <string name="menu_create_dir" msgid="2547620241173881754">"Yangi jild"</string>
     <string name="menu_grid" msgid="6878021334497835259">"Katak ko‘rinishida"</string>
     <string name="menu_list" msgid="7279285939892417279">"Ro‘yxat ko‘rinishida"</string>
     <string name="menu_sort" msgid="7677740407158414452">"Saralash"</string>
@@ -33,6 +33,7 @@
     <string name="menu_select_all" msgid="8323579667348729928">"Barchasini belgilash"</string>
     <string name="menu_copy" msgid="3612326052677229148">"Nusxalash…"</string>
     <string name="menu_move" msgid="1828090633118079817">"Ko‘chirib o‘tkazish…"</string>
+    <string name="menu_new_window" msgid="1226032889278727538">"Yangi oyna"</string>
     <string name="menu_copy_to_clipboard" msgid="489311381979634291">"Nusxalash"</string>
     <string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Joylash"</string>
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Ichki xotirani ko‘rsatish"</string>
@@ -45,8 +46,7 @@
     <string name="button_copy" msgid="8706475544635021302">"Nusxalash"</string>
     <string name="button_move" msgid="2202666023104202232">"Ko‘chirib o‘tkazish"</string>
     <string name="button_dismiss" msgid="3714065566893946085">"O‘chirish"</string>
-    <!-- no translation found for button_retry (4392027584153752797) -->
-    <skip />
+    <string name="button_retry" msgid="4392027584153752797">"Qayta urinish"</string>
     <string name="sort_name" msgid="9183560467917256779">"Nomi bo‘yicha"</string>
     <string name="sort_date" msgid="586080032956151448">"Tahrir sanasi bo‘yicha"</string>
     <string name="sort_size" msgid="3350681319735474741">"Hajmi bo‘yicha"</string>
diff --git a/packages/DocumentsUI/res/values-vi/strings.xml b/packages/DocumentsUI/res/values-vi/strings.xml
index 4583362..52a4e82 100644
--- a/packages/DocumentsUI/res/values-vi/strings.xml
+++ b/packages/DocumentsUI/res/values-vi/strings.xml
@@ -20,7 +20,7 @@
     <string name="files_label" msgid="6051402950202690279">"Tệp"</string>
     <string name="title_open" msgid="4353228937663917801">"Mở từ"</string>
     <string name="title_save" msgid="2433679664882857999">"Lưu vào"</string>
-    <string name="menu_create_dir" msgid="5947289605844398389">"Tạo thư mục"</string>
+    <string name="menu_create_dir" msgid="2547620241173881754">"Thư mục mới"</string>
     <string name="menu_grid" msgid="6878021334497835259">"Chế độ xem lưới"</string>
     <string name="menu_list" msgid="7279285939892417279">"Chế độ xem danh sách"</string>
     <string name="menu_sort" msgid="7677740407158414452">"Sắp xếp theo"</string>
@@ -33,6 +33,7 @@
     <string name="menu_select_all" msgid="8323579667348729928">"Chọn tất cả"</string>
     <string name="menu_copy" msgid="3612326052677229148">"Sao chép vào…"</string>
     <string name="menu_move" msgid="1828090633118079817">"Chuyển tới..."</string>
+    <string name="menu_new_window" msgid="1226032889278727538">"Cửa sổ mới"</string>
     <string name="menu_copy_to_clipboard" msgid="489311381979634291">"Sao chép"</string>
     <string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Dán"</string>
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Hiển thị bộ nhớ trong"</string>
@@ -45,8 +46,7 @@
     <string name="button_copy" msgid="8706475544635021302">"Sao chép"</string>
     <string name="button_move" msgid="2202666023104202232">"Di chuyển"</string>
     <string name="button_dismiss" msgid="3714065566893946085">"Loại bỏ"</string>
-    <!-- no translation found for button_retry (4392027584153752797) -->
-    <skip />
+    <string name="button_retry" msgid="4392027584153752797">"Thử lại"</string>
     <string name="sort_name" msgid="9183560467917256779">"Theo tên"</string>
     <string name="sort_date" msgid="586080032956151448">"Theo ngày sửa đổi"</string>
     <string name="sort_size" msgid="3350681319735474741">"Theo kích thước"</string>
diff --git a/packages/DocumentsUI/res/values-zh-rCN/strings.xml b/packages/DocumentsUI/res/values-zh-rCN/strings.xml
index d577e14..8ee90cc 100644
--- a/packages/DocumentsUI/res/values-zh-rCN/strings.xml
+++ b/packages/DocumentsUI/res/values-zh-rCN/strings.xml
@@ -20,7 +20,7 @@
     <string name="files_label" msgid="6051402950202690279">"文件"</string>
     <string name="title_open" msgid="4353228937663917801">"打开文件"</string>
     <string name="title_save" msgid="2433679664882857999">"保存文件"</string>
-    <string name="menu_create_dir" msgid="5947289605844398389">"新建文件夹"</string>
+    <string name="menu_create_dir" msgid="2547620241173881754">"新建文件夹"</string>
     <string name="menu_grid" msgid="6878021334497835259">"网格视图"</string>
     <string name="menu_list" msgid="7279285939892417279">"列表视图"</string>
     <string name="menu_sort" msgid="7677740407158414452">"排序依据"</string>
@@ -33,6 +33,7 @@
     <string name="menu_select_all" msgid="8323579667348729928">"全选"</string>
     <string name="menu_copy" msgid="3612326052677229148">"复制到…"</string>
     <string name="menu_move" msgid="1828090633118079817">"移动到…"</string>
+    <string name="menu_new_window" msgid="1226032889278727538">"新建窗口"</string>
     <string name="menu_copy_to_clipboard" msgid="489311381979634291">"复制"</string>
     <string name="menu_paste_from_clipboard" msgid="2071583031180257091">"粘贴"</string>
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"显示内部存储设备"</string>
@@ -45,8 +46,7 @@
     <string name="button_copy" msgid="8706475544635021302">"复制"</string>
     <string name="button_move" msgid="2202666023104202232">"移动"</string>
     <string name="button_dismiss" msgid="3714065566893946085">"关闭"</string>
-    <!-- no translation found for button_retry (4392027584153752797) -->
-    <skip />
+    <string name="button_retry" msgid="4392027584153752797">"重试"</string>
     <string name="sort_name" msgid="9183560467917256779">"按名称"</string>
     <string name="sort_date" msgid="586080032956151448">"按修改日期"</string>
     <string name="sort_size" msgid="3350681319735474741">"按大小"</string>
diff --git a/packages/DocumentsUI/res/values-zh-rHK/strings.xml b/packages/DocumentsUI/res/values-zh-rHK/strings.xml
index 3d44047..dadc029 100644
--- a/packages/DocumentsUI/res/values-zh-rHK/strings.xml
+++ b/packages/DocumentsUI/res/values-zh-rHK/strings.xml
@@ -20,7 +20,7 @@
     <string name="files_label" msgid="6051402950202690279">"檔案"</string>
     <string name="title_open" msgid="4353228937663917801">"開啟檔案"</string>
     <string name="title_save" msgid="2433679664882857999">"儲存至"</string>
-    <string name="menu_create_dir" msgid="5947289605844398389">"建立資料夾"</string>
+    <string name="menu_create_dir" msgid="2547620241173881754">"新增資料夾"</string>
     <string name="menu_grid" msgid="6878021334497835259">"格狀檢視"</string>
     <string name="menu_list" msgid="7279285939892417279">"清單檢視"</string>
     <string name="menu_sort" msgid="7677740407158414452">"排序方式"</string>
@@ -33,6 +33,7 @@
     <string name="menu_select_all" msgid="8323579667348729928">"全部選取"</string>
     <string name="menu_copy" msgid="3612326052677229148">"複製到…"</string>
     <string name="menu_move" msgid="1828090633118079817">"移至…"</string>
+    <string name="menu_new_window" msgid="1226032889278727538">"新視窗"</string>
     <string name="menu_copy_to_clipboard" msgid="489311381979634291">"複製"</string>
     <string name="menu_paste_from_clipboard" msgid="2071583031180257091">"貼上"</string>
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"顯示內部儲存空間"</string>
@@ -45,8 +46,7 @@
     <string name="button_copy" msgid="8706475544635021302">"複製"</string>
     <string name="button_move" msgid="2202666023104202232">"移動"</string>
     <string name="button_dismiss" msgid="3714065566893946085">"關閉"</string>
-    <!-- no translation found for button_retry (4392027584153752797) -->
-    <skip />
+    <string name="button_retry" msgid="4392027584153752797">"再試一次"</string>
     <string name="sort_name" msgid="9183560467917256779">"按名稱"</string>
     <string name="sort_date" msgid="586080032956151448">"按修改日期"</string>
     <string name="sort_size" msgid="3350681319735474741">"按大小"</string>
diff --git a/packages/DocumentsUI/res/values-zh-rTW/strings.xml b/packages/DocumentsUI/res/values-zh-rTW/strings.xml
index aaad98c..cdd1288 100644
--- a/packages/DocumentsUI/res/values-zh-rTW/strings.xml
+++ b/packages/DocumentsUI/res/values-zh-rTW/strings.xml
@@ -20,7 +20,7 @@
     <string name="files_label" msgid="6051402950202690279">"檔案"</string>
     <string name="title_open" msgid="4353228937663917801">"開啟檔案"</string>
     <string name="title_save" msgid="2433679664882857999">"儲存至"</string>
-    <string name="menu_create_dir" msgid="5947289605844398389">"建立資料夾"</string>
+    <string name="menu_create_dir" msgid="2547620241173881754">"新增資料夾"</string>
     <string name="menu_grid" msgid="6878021334497835259">"格狀檢視"</string>
     <string name="menu_list" msgid="7279285939892417279">"清單檢視"</string>
     <string name="menu_sort" msgid="7677740407158414452">"排序依據"</string>
@@ -33,6 +33,7 @@
     <string name="menu_select_all" msgid="8323579667348729928">"全選"</string>
     <string name="menu_copy" msgid="3612326052677229148">"複製到…"</string>
     <string name="menu_move" msgid="1828090633118079817">"移至…"</string>
+    <string name="menu_new_window" msgid="1226032889278727538">"新視窗"</string>
     <string name="menu_copy_to_clipboard" msgid="489311381979634291">"複製"</string>
     <string name="menu_paste_from_clipboard" msgid="2071583031180257091">"貼上"</string>
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"顯示內部儲存空間"</string>
@@ -45,8 +46,7 @@
     <string name="button_copy" msgid="8706475544635021302">"複製"</string>
     <string name="button_move" msgid="2202666023104202232">"移動"</string>
     <string name="button_dismiss" msgid="3714065566893946085">"關閉"</string>
-    <!-- no translation found for button_retry (4392027584153752797) -->
-    <skip />
+    <string name="button_retry" msgid="4392027584153752797">"再試一次"</string>
     <string name="sort_name" msgid="9183560467917256779">"依名稱"</string>
     <string name="sort_date" msgid="586080032956151448">"依修改日期"</string>
     <string name="sort_size" msgid="3350681319735474741">"依大小"</string>
diff --git a/packages/DocumentsUI/res/values-zu/strings.xml b/packages/DocumentsUI/res/values-zu/strings.xml
index 8f20cc4..d6bb2b4 100644
--- a/packages/DocumentsUI/res/values-zu/strings.xml
+++ b/packages/DocumentsUI/res/values-zu/strings.xml
@@ -20,7 +20,7 @@
     <string name="files_label" msgid="6051402950202690279">"Amafayela"</string>
     <string name="title_open" msgid="4353228937663917801">"Vula kusuka ku-"</string>
     <string name="title_save" msgid="2433679664882857999">"Londoloza ku-"</string>
-    <string name="menu_create_dir" msgid="5947289605844398389">"Dala ifolda"</string>
+    <string name="menu_create_dir" msgid="2547620241173881754">"Ifolda entsha"</string>
     <string name="menu_grid" msgid="6878021334497835259">"Ukubuka kwegridi"</string>
     <string name="menu_list" msgid="7279285939892417279">"Ukubuka uhlu"</string>
     <string name="menu_sort" msgid="7677740407158414452">"Hlunga nge-"</string>
@@ -33,6 +33,7 @@
     <string name="menu_select_all" msgid="8323579667348729928">"Khetha konke"</string>
     <string name="menu_copy" msgid="3612326052677229148">"Kopishela ku…"</string>
     <string name="menu_move" msgid="1828090633118079817">"Hambisa ku…"</string>
+    <string name="menu_new_window" msgid="1226032889278727538">"Iwindi elisha"</string>
     <string name="menu_copy_to_clipboard" msgid="489311381979634291">"Kopisha"</string>
     <string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Namathisela"</string>
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Bonisa isitoreji sangaphakathi"</string>
diff --git a/packages/DocumentsUI/res/values/config.xml b/packages/DocumentsUI/res/values/config.xml
index ce5b174..ed7820b 100644
--- a/packages/DocumentsUI/res/values/config.xml
+++ b/packages/DocumentsUI/res/values/config.xml
@@ -15,5 +15,5 @@
 -->
 
 <resources>
-    <bool name="productivity_device">false</bool>
+    <bool name="productivity_device">true</bool>
 </resources>
diff --git a/packages/DocumentsUI/res/values/strings.xml b/packages/DocumentsUI/res/values/strings.xml
index a4acb60..d21b5ee 100644
--- a/packages/DocumentsUI/res/values/strings.xml
+++ b/packages/DocumentsUI/res/values/strings.xml
@@ -27,7 +27,7 @@
     <string name="title_save">Save to</string>
 
     <!-- Menu item that creates a new directory/folder at the current location [CHAR LIMIT=24] -->
-    <string name="menu_create_dir">Create folder</string>
+    <string name="menu_create_dir">New folder</string>
     <!-- Menu item that switches view to show documents as a large-format grid of thumbnails [CHAR LIMIT=24] -->
     <string name="menu_grid">Grid view</string>
     <!-- Menu item that switches view to show documents as a list [CHAR LIMIT=24] -->
diff --git a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
index a6a45e5..9d2d171 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
@@ -20,6 +20,7 @@
 import static com.android.documentsui.DirectoryFragment.ANIM_NONE;
 import static com.android.documentsui.DirectoryFragment.ANIM_SIDE;
 import static com.android.documentsui.DirectoryFragment.ANIM_UP;
+import static com.android.documentsui.Shared.DEBUG;
 import static com.android.internal.util.Preconditions.checkArgument;
 
 import android.app.Activity;
@@ -38,6 +39,7 @@
 import android.support.annotation.LayoutRes;
 import android.support.annotation.Nullable;
 import android.util.Log;
+import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.Menu;
 import android.view.MenuItem;
@@ -127,10 +129,10 @@
 
     @Override
     public boolean onPrepareOptionsMenu(Menu menu) {
-        boolean shown = super.onPrepareOptionsMenu(menu);
+        super.onPrepareOptionsMenu(menu);
 
         final RootInfo root = getCurrentRoot();
-        final DocumentInfo cwd = getCurrentDirectory();
+        final boolean inRecents = getCurrentDirectory() == null;
 
         final MenuItem sort = menu.findItem(R.id.menu_sort);
         final MenuItem sortSize = menu.findItem(R.id.menu_sort_size);
@@ -140,26 +142,28 @@
         final MenuItem fileSize = menu.findItem(R.id.menu_file_size);
         final MenuItem settings = menu.findItem(R.id.menu_settings);
 
-        mSearchManager.update(root);
+        // I'm thinkin' this isn't necesary here. If it is...'cuz of a bug....
+        // then uncomment the linke and let's get a proper bug reference here.
+        // mSearchManager.update(root);
 
         // Search uses backend ranking; no sorting
-        sort.setVisible(cwd != null && !mSearchManager.isSearching());
+        sort.setVisible(!inRecents && !mSearchManager.isSearching());
+
+        // grid/list is effectively a toggle.
+        grid.setVisible(mState.derivedMode != State.MODE_GRID);
+        list.setVisible(mState.derivedMode != State.MODE_LIST);
+
+        sortSize.setVisible(mState.showSize); // Only sort by size when visible
+        fileSize.setVisible(!mState.forceSize);
+        advanced.setVisible(!mState.forceAdvanced);
+        settings.setVisible((root.flags & Root.FLAG_HAS_SETTINGS) != 0);
 
         advanced.setTitle(LocalPreferences.getDisplayAdvancedDevices(this)
                 ? R.string.menu_advanced_hide : R.string.menu_advanced_show);
         fileSize.setTitle(LocalPreferences.getDisplayFileSize(this)
                 ? R.string.menu_file_size_hide : R.string.menu_file_size_show);
 
-        State state = getDisplayState();
-
-        sortSize.setVisible(state.showSize); // Only sort by size when visible
-        fileSize.setVisible(!state.showSize);
-        grid.setVisible(state.derivedMode != State.MODE_GRID);
-        list.setVisible(state.derivedMode != State.MODE_LIST);
-        advanced.setVisible(!mState.showAdvanced);
-        settings.setVisible((root.flags & Root.FLAG_HAS_SETTINGS) != 0);
-
-        return shown;
+        return true;
     }
 
     State buildDefaultState() {
@@ -185,12 +189,10 @@
     void onStackRestored(boolean restored, boolean external) {}
 
     void onRootPicked(RootInfo root) {
-        State state = getDisplayState();
-
         // Clear entire backstack and start in new root
-        state.stack.root = root;
-        state.stack.clear();
-        state.stackTouched = true;
+        mState.stack.root = root;
+        mState.stack.clear();
+        mState.stackTouched = true;
 
         mSearchManager.update(root);
 
@@ -280,6 +282,7 @@
         return cwd != null
                 && cwd.isCreateSupported()
                 && !mSearchManager.isSearching()
+                && !root.isRecents()
                 && !root.isDownloads();
     }
 
@@ -289,8 +292,8 @@
     }
 
     void openDirectory(DocumentInfo doc) {
-        getDisplayState().stack.push(doc);
-        getDisplayState().stackTouched = true;
+        mState.stack.push(doc);
+        mState.stackTouched = true;
         onCurrentDirectoryChanged(ANIM_DOWN);
     }
 
@@ -367,16 +370,15 @@
     }
 
     void setDisplayAdvancedDevices(boolean display) {
-        State state = getDisplayState();
         LocalPreferences.setDisplayAdvancedDevices(this, display);
-        state.showAdvanced = state.forceAdvanced | display;
+        mState.showAdvanced = mState.forceAdvanced | display;
         RootsFragment.get(getFragmentManager()).onDisplayStateChanged();
         invalidateOptionsMenu();
     }
 
     void setDisplayFileSize(boolean display) {
         LocalPreferences.setDisplayFileSize(this, display);
-        getDisplayState().showSize = display;
+        mState.showSize = display;
         DirectoryFragment.get(getFragmentManager()).onDisplayStateChanged();
         invalidateOptionsMenu();
     }
@@ -389,7 +391,7 @@
      * Set state sort order based on explicit user action.
      */
     void setUserSortOrder(int sortOrder) {
-        getDisplayState().userSortOrder = sortOrder;
+        mState.userSortOrder = sortOrder;
         DirectoryFragment.get(getFragmentManager()).onUserSortOrderChanged();
     }
 
@@ -397,7 +399,7 @@
      * Set state mode based on explicit user action.
      */
     void setUserMode(int mode) {
-        getDisplayState().userMode = mode;
+        mState.userMode = mode;
         DirectoryFragment.get(getFragmentManager()).onUserModeChanged();
     }
 
@@ -411,7 +413,7 @@
     @Override
     protected void onSaveInstanceState(Bundle state) {
         super.onSaveInstanceState(state);
-        state.putParcelable(EXTRA_STATE, getDisplayState());
+        state.putParcelable(EXTRA_STATE, mState);
     }
 
     @Override
@@ -420,16 +422,15 @@
     }
 
     RootInfo getCurrentRoot() {
-        State state = getDisplayState();
-        if (state.stack.root != null) {
-            return state.stack.root;
+        if (mState.stack.root != null) {
+            return mState.stack.root;
         } else {
             return mRoots.getRecentsRoot();
         }
     }
 
     public DocumentInfo getCurrentDirectory() {
-        return getDisplayState().stack.peek();
+        return mState.stack.peek();
     }
 
     public Executor getExecutorForCurrentDirectory() {
@@ -465,14 +466,28 @@
         }
     }
 
+    @Override
+    public boolean onKeyUp(int keyCode, KeyEvent event) {
+        if (DEBUG) Log.d(mTag, "onKeyUp: keycode = " + keyCode);
+        DirectoryFragment dir = DirectoryFragment.get(getFragmentManager());
+        switch (keyCode) {
+            case KeyEvent.KEYCODE_MOVE_HOME:
+                dir.focusFirstFile();
+                return true;
+            case KeyEvent.KEYCODE_MOVE_END:
+                dir.focusLastFile();
+                return true;
+        }
+        return super.onKeyUp(keyCode, event);
+    }
+
     public void onStackPicked(DocumentStack stack) {
         try {
             // Update the restored stack to ensure we have freshest data
             stack.updateDocuments(getContentResolver());
 
-            State state = getDisplayState();
-            state.stack = stack;
-            state.stackTouched = true;
+            mState.stack = stack;
+            mState.stackTouched = true;
             onCurrentDirectoryChanged(ANIM_SIDE);
 
         } catch (FileNotFoundException e) {
@@ -502,9 +517,8 @@
         @Override
         protected void onPostExecute(DocumentInfo result) {
             if (result != null) {
-                State state = getDisplayState();
-                state.stack.push(result);
-                state.stackTouched = true;
+                mState.stack.push(result);
+                mState.stackTouched = true;
                 onCurrentDirectoryChanged(ANIM_SIDE);
             }
         }
@@ -516,7 +530,9 @@
 
         @Override
         protected Void doInBackground(Void... params) {
-            State state = getDisplayState();
+            if (DEBUG && !mState.stack.isEmpty()) {
+                Log.w(mTag, "Overwriting existing stack.");
+            }
             RootsCache roots = DocumentsApplication.getRootsCache(BaseActivity.this);
 
             // Restore last stack for calling package
@@ -528,7 +544,7 @@
                     mExternal = cursor.getInt(cursor.getColumnIndex(ResumeColumns.EXTERNAL)) != 0;
                     final byte[] rawStack = cursor.getBlob(
                             cursor.getColumnIndex(ResumeColumns.STACK));
-                    DurableUtils.readFromArray(rawStack, state.stack);
+                    DurableUtils.readFromArray(rawStack, mState.stack);
                     mRestoredStack = true;
                 }
             } catch (IOException e) {
@@ -539,13 +555,13 @@
 
             if (mRestoredStack) {
                 // Update the restored stack to ensure we have freshest data
-                final Collection<RootInfo> matchingRoots = roots.getMatchingRootsBlocking(state);
+                final Collection<RootInfo> matchingRoots = roots.getMatchingRootsBlocking(mState);
                 try {
-                    state.stack.updateRoot(matchingRoots);
-                    state.stack.updateDocuments(getContentResolver());
+                    mState.stack.updateRoot(matchingRoots);
+                    mState.stack.updateDocuments(getContentResolver());
                 } catch (FileNotFoundException e) {
                     Log.w(mTag, "Failed to restore stack: " + e);
-                    state.stack.reset();
+                    mState.stack.reset();
                     mRestoredStack = false;
                 }
             }
@@ -556,7 +572,7 @@
         @Override
         protected void onPostExecute(Void result) {
             if (isDestroyed()) return;
-            getDisplayState().restored = true;
+            mState.restored = true;
             onCurrentDirectoryChanged(ANIM_NONE);
             onStackRestored(mRestoredStack, mExternal);
         }
@@ -600,10 +616,9 @@
                 return;
             }
 
-            State state = getDisplayState();
-            while (state.stack.size() > position + 1) {
-                state.stackTouched = true;
-                state.stack.pop();
+            while (mState.stack.size() > position + 1) {
+                mState.stackTouched = true;
+                mState.stack.pop();
             }
             onCurrentDirectoryChanged(ANIM_UP);
         }
@@ -620,13 +635,12 @@
     final class StackAdapter extends BaseAdapter {
         @Override
         public int getCount() {
-            return getDisplayState().stack.size();
+            return mState.stack.size();
         }
 
         @Override
         public DocumentInfo getItem(int position) {
-            State state = getDisplayState();
-            return state.stack.get(state.stack.size() - position - 1);
+            return mState.stack.get(mState.stack.size() - position - 1);
         }
 
         @Override
@@ -714,13 +728,12 @@
                 return;
             }
 
-            State state = getDisplayState();
-            if (state.currentSearch != null) {
+            if (mState.currentSearch != null) {
                 mMenu.expandActionView();
 
                 mView.setIconified(false);
                 mView.clearFocus();
-                mView.setQuery(state.currentSearch, false);
+                mView.setQuery(mState.currentSearch, false);
             } else {
                 mView.clearFocus();
                 if (!mView.isIconified()) {
@@ -746,7 +759,7 @@
 
             mMenu.setVisible(visible);
             if (!visible) {
-                getDisplayState().currentSearch = null;
+                mState.currentSearch = null;
             }
         }
 
@@ -764,7 +777,7 @@
         }
 
         boolean isSearching() {
-            return getDisplayState().currentSearch != null;
+            return mState.currentSearch != null;
         }
 
         boolean isExpanded() {
@@ -779,7 +792,7 @@
                 return false;
             }
 
-            getDisplayState().currentSearch = null;
+            mState.currentSearch = null;
             onCurrentDirectoryChanged(ANIM_NONE);
             return false;
         }
@@ -798,7 +811,7 @@
                 mIgnoreNextCollapse = false;
                 return true;
             }
-            getDisplayState().currentSearch = null;
+            mState.currentSearch = null;
             onCurrentDirectoryChanged(ANIM_NONE);
             return true;
         }
@@ -806,7 +819,7 @@
         @Override
         public boolean onQueryTextSubmit(String query) {
             mSearchExpanded = true;
-            getDisplayState().currentSearch = query;
+            mState.currentSearch = query;
             mView.clearFocus();
             onCurrentDirectoryChanged(ANIM_NONE);
             return true;
diff --git a/packages/DocumentsUI/src/com/android/documentsui/CopyService.java b/packages/DocumentsUI/src/com/android/documentsui/CopyService.java
index 815fbfe..047949f 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/CopyService.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/CopyService.java
@@ -34,6 +34,7 @@
 import android.os.CancellationSignal;
 import android.os.ParcelFileDescriptor;
 import android.os.Parcelable;
+import android.os.PowerManager;
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.provider.DocumentsContract;
@@ -62,7 +63,6 @@
 
     private static final String EXTRA_CANCEL = "com.android.documentsui.CANCEL";
     public static final String EXTRA_SRC_LIST = "com.android.documentsui.SRC_LIST";
-    public static final String EXTRA_STACK = "com.android.documentsui.STACK";
     public static final String EXTRA_FAILURE = "com.android.documentsui.FAILURE";
     public static final String EXTRA_TRANSFER_MODE = "com.android.documentsui.TRANSFER_MODE";
 
@@ -73,6 +73,8 @@
     // TODO: Move it to a shared file when more operations are implemented.
     public static final int FAILURE_COPY = 1;
 
+    private PowerManager mPowerManager;
+
     private NotificationManager mNotificationManager;
     private Notification.Builder mProgressBuilder;
 
@@ -115,12 +117,12 @@
         final Intent copyIntent = new Intent(activity, CopyService.class);
         copyIntent.putParcelableArrayListExtra(
                 EXTRA_SRC_LIST, new ArrayList<DocumentInfo>(srcDocs));
-        copyIntent.putExtra(EXTRA_STACK, (Parcelable) dstStack);
+        copyIntent.putExtra(Shared.EXTRA_STACK, (Parcelable) dstStack);
         copyIntent.putExtra(EXTRA_TRANSFER_MODE, mode);
 
         int toastMessage = (mode == TRANSFER_MODE_COPY) ? R.plurals.copy_begin
                 : R.plurals.move_begin;
-        Shared.makeSnackbar(activity,
+        Snackbars.makeSnackbar(activity,
                 res.getQuantityString(toastMessage, srcDocs.size(), srcDocs.size()),
                 Snackbar.LENGTH_SHORT).show();
         activity.startService(copyIntent);
@@ -141,12 +143,16 @@
             return;
         }
 
+        final PowerManager.WakeLock wakeLock = mPowerManager
+                .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
         final ArrayList<DocumentInfo> srcs = intent.getParcelableArrayListExtra(EXTRA_SRC_LIST);
-        final DocumentStack stack = intent.getParcelableExtra(EXTRA_STACK);
+        final DocumentStack stack = intent.getParcelableExtra(Shared.EXTRA_STACK);
         // Copy by default.
         final int transferMode = intent.getIntExtra(EXTRA_TRANSFER_MODE, TRANSFER_MODE_COPY);
 
         try {
+            wakeLock.acquire();
+
             // Acquire content providers.
             mSrcClient = DocumentsApplication.acquireUnstableProviderOrThrow(getContentResolver(),
                     srcs.get(0).authority);
@@ -166,6 +172,8 @@
             ContentProviderClient.releaseQuietly(mSrcClient);
             ContentProviderClient.releaseQuietly(mDstClient);
 
+            wakeLock.release();
+
             // Dismiss the ongoing copy notification when the copy is done.
             mNotificationManager.cancel(mJobId, 0);
 
@@ -173,7 +181,7 @@
                 Log.e(TAG, mFailedFiles.size() + " files failed to copy");
                 final Context context = getApplicationContext();
                 final Intent navigateIntent = new Intent(context, FilesActivity.class);
-                navigateIntent.putExtra(EXTRA_STACK, (Parcelable) stack);
+                navigateIntent.putExtra(Shared.EXTRA_STACK, (Parcelable) stack);
                 navigateIntent.putExtra(EXTRA_FAILURE, FAILURE_COPY);
                 navigateIntent.putExtra(EXTRA_TRANSFER_MODE, transferMode);
                 navigateIntent.putParcelableArrayListExtra(EXTRA_SRC_LIST, mFailedFiles);
@@ -199,7 +207,8 @@
     @Override
     public void onCreate() {
         super.onCreate();
-        mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+        mPowerManager = getSystemService(PowerManager.class);
+        mNotificationManager = getSystemService(NotificationManager.class);
     }
 
     /**
@@ -221,7 +230,7 @@
 
         final Context context = getApplicationContext();
         final Intent navigateIntent = new Intent(context, FilesActivity.class);
-        navigateIntent.putExtra(EXTRA_STACK, (Parcelable) stack);
+        navigateIntent.putExtra(Shared.EXTRA_STACK, (Parcelable) stack);
 
         final String contentTitle = getString(copying ? R.string.copy_notification_title
                 : R.string.move_notification_title);
diff --git a/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java
index 9f44516..c6425a6 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java
@@ -147,7 +147,7 @@
                 // Navigate into newly created child
                 mActivity.onDirectoryCreated(result);
             } else {
-                Shared.makeSnackbar(mActivity, R.string.create_error, Snackbar.LENGTH_SHORT).show();
+                Snackbars.makeSnackbar(mActivity, R.string.create_error, Snackbar.LENGTH_SHORT).show();
             }
 
             mActivity.setPending(false);
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
index 3c6be6e..2fe829f 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
@@ -44,7 +44,6 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.Loader;
-import android.content.res.Resources;
 import android.database.Cursor;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
@@ -54,7 +53,6 @@
 import android.os.AsyncTask;
 import android.os.Bundle;
 import android.os.CancellationSignal;
-import android.os.Handler;
 import android.os.Looper;
 import android.os.OperationCanceledException;
 import android.os.Parcelable;
@@ -134,8 +132,6 @@
     private Model mModel;
     private Model.UpdateListener mModelUpdateListener = new ModelUpdateListener();
 
-    private final Handler mHandler = new Handler(Looper.getMainLooper());
-
     private View mEmptyView;
     private RecyclerView mRecView;
 
@@ -217,8 +213,6 @@
     @Override
     public View onCreateView(
             LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
-        final Context context = inflater.getContext();
-        final Resources res = context.getResources();
         final View view = inflater.inflate(R.layout.fragment_directory, container, false);
 
         mMessageBar = MessageBar.create(getChildFragmentManager());
@@ -423,7 +417,7 @@
         }
 
         CopyService.start(getActivity(), getDisplayState(this).selectedDocumentsForCopy,
-                (DocumentStack) data.getParcelableExtra(CopyService.EXTRA_STACK),
+                (DocumentStack) data.getParcelableExtra(Shared.EXTRA_STACK),
                 data.getIntExtra(CopyService.EXTRA_TRANSFER_MODE, CopyService.TRANSFER_MODE_NONE));
     }
 
@@ -678,6 +672,7 @@
             checkNotNull(mMenu);
             // Delegate update logic to our owning action, since specialized logic is desired.
             mFragmentTuner.updateActionMenu(mMenu, mType, mNoDeleteCount == 0);
+            Menus.disableHiddenItems(mMenu);
         }
 
         @Override
@@ -799,13 +794,12 @@
 
     private void deleteDocuments(final Selection selected) {
         Context context = getActivity();
-        ContentResolver resolver = context.getContentResolver();
         String message = Shared.getQuantityString(context, R.plurals.deleting, selected.size());
 
         mModel.markForDeletion(selected);
 
         final Activity activity = getActivity();
-        Shared.makeSnackbar(activity, message, Snackbar.LENGTH_LONG)
+        Snackbars.makeSnackbar(activity, message, Snackbar.LENGTH_LONG)
                 .setAction(
                         R.string.undo,
                         new android.view.View.OnClickListener() {
@@ -823,7 +817,7 @@
                                             new Model.DeletionListener() {
                                                 @Override
                                                 public void onError() {
-                                                    Shared.makeSnackbar(
+                                                    Snackbars.makeSnackbar(
                                                             activity,
                                                             R.string.toast_failed_delete,
                                                             Snackbar.LENGTH_LONG)
@@ -879,6 +873,10 @@
         public DocumentHolder(View view) {
             super(view);
             this.view = view;
+            // Setting this using android:focusable in the item layouts doesn't work for list items.
+            // So we set it here.  Note that touch mode focus is a separate issue - see
+            // View.setFocusableInTouchMode and View.isInTouchMode for more info.
+            this.view.setFocusable(true);
         }
     }
 
@@ -1244,7 +1242,7 @@
 
     private void copyDocuments(final List<DocumentInfo> docs, final DocumentInfo destination) {
         if (!canCopy(docs, destination)) {
-            Shared.makeSnackbar(
+            Snackbars.makeSnackbar(
                     getActivity(),
                     R.string.clipboard_files_cannot_paste,
                     Snackbar.LENGTH_SHORT)
@@ -1298,7 +1296,7 @@
             void onDocumentsReady(List<DocumentInfo> docs) {
                 mClipper.clipDocuments(docs);
                 Activity activity = getActivity();
-                Shared.makeSnackbar(activity,
+                Snackbars.makeSnackbar(activity,
                         activity.getResources().getQuantityString(
                                 R.plurals.clipboard_files_clipped, docs.size(), docs.size()),
                                 Snackbar.LENGTH_SHORT).show();
@@ -1343,6 +1341,45 @@
         }
     }
 
+    /**
+     * Scrolls to the top of the file list and focuses the first file.
+     */
+    void focusFirstFile() {
+        focusFile(0);
+    }
+
+    /**
+     * Scrolls to the bottom of the file list and focuses the last file.
+     */
+    void focusLastFile() {
+        focusFile(mAdapter.getItemCount() - 1);
+    }
+
+    /**
+     * Scrolls to and then focuses on the file at the given position.
+     */
+    private void focusFile(final int pos) {
+        // Don't smooth scroll; that taxes the system unnecessarily and makes the scroll handling
+        // logic below more complicated.
+        mRecView.scrollToPosition(pos);
+
+        // If the item is already in view, focus it; otherwise, set a one-time listener to focus it
+        // when the scroll is completed.
+        RecyclerView.ViewHolder vh = mRecView.findViewHolderForAdapterPosition(pos);
+        if (vh != null) {
+            vh.itemView.requestFocus();
+        } else {
+            mRecView.addOnScrollListener(
+                    new RecyclerView.OnScrollListener() {
+                        @Override
+                        public void onScrolled(RecyclerView view, int dx, int dy) {
+                            view.findViewHolderForAdapterPosition(pos).itemView.requestFocus();
+                            view.removeOnScrollListener(this);
+                        }
+                    });
+        }
+    }
+
     private void setupDragAndDropOnDirectoryView(View view) {
         // Listen for drops on non-directory items and empty space.
         view.setOnDragListener(mOnDragListener);
@@ -1608,23 +1645,25 @@
 
         @Override
         public void updateActionMenu(Menu menu, int dirType, boolean canDelete) {
+            boolean copyEnabled = mManaging && dirType != TYPE_RECENT_OPEN;
+            // TODO: The selection needs to be deletable.
+            boolean moveEnabled =
+                    SystemProperties.getBoolean("debug.documentsui.enable_move", false);
+            menu.findItem(R.id.menu_copy_to_clipboard).setEnabled(copyEnabled);
 
             final MenuItem open = menu.findItem(R.id.menu_open);
             final MenuItem share = menu.findItem(R.id.menu_share);
             final MenuItem delete = menu.findItem(R.id.menu_delete);
             final MenuItem copyTo = menu.findItem(R.id.menu_copy_to);
             final MenuItem moveTo = menu.findItem(R.id.menu_move_to);
-            final MenuItem copyToClipboard = menu.findItem(R.id.menu_copy_to_clipboard);
 
             open.setVisible(!mManaging);
             share.setVisible(mManaging);
             delete.setVisible(mManaging && canDelete);
-            // Disable copying from the Recents view.
-            copyTo.setVisible(mManaging && dirType != TYPE_RECENT_OPEN);
-            moveTo.setVisible(SystemProperties.getBoolean("debug.documentsui.enable_move", false));
-
-            // Only shown in files mode.
-            copyToClipboard.setVisible(false);
+            copyTo.setVisible(copyEnabled);
+            copyTo.setEnabled(copyEnabled);
+            moveTo.setVisible(moveEnabled);
+            moveTo.setEnabled(moveEnabled);
         }
 
         @Override
@@ -1638,13 +1677,14 @@
         @Override
         public void updateActionMenu(Menu menu, int dirType, boolean canDelete) {
 
+            menu.findItem(R.id.menu_copy_to_clipboard).setEnabled(dirType != TYPE_RECENT_OPEN);
+
             menu.findItem(R.id.menu_share).setVisible(true);
             menu.findItem(R.id.menu_delete).setVisible(canDelete);
-            menu.findItem(R.id.menu_copy_to_clipboard).setVisible(true);
 
             menu.findItem(R.id.menu_open).setVisible(false);
-            menu.findItem(R.id.menu_copy_to).setVisible(false);
-            menu.findItem(R.id.menu_move_to).setVisible(false);
+            menu.findItem(R.id.menu_copy_to).setVisible(true);
+            menu.findItem(R.id.menu_move_to).setVisible(true);
         }
 
         @Override
@@ -1756,7 +1796,7 @@
                 }
             }
 
-            if (DEBUG) {
+            if (DEBUG && position != originalPos) {
                 Log.d(TAG, "Item position adjusted for deletion.  Original: " + originalPos
                         + "  Adjusted: " + position);
             }
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
index f6ded4b..6b428f5 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
@@ -89,8 +89,6 @@
             setTheme(R.style.DocumentsNonDialogTheme);
         }
 
-        final Context context = this;
-
         if (mShowAsDialog) {
             mDrawer = DrawerController.createDummy();
 
@@ -314,41 +312,35 @@
     public boolean onPrepareOptionsMenu(Menu menu) {
         super.onPrepareOptionsMenu(menu);
 
-        final RootInfo root = getCurrentRoot();
         final DocumentInfo cwd = getCurrentDirectory();
 
         final MenuItem createDir = menu.findItem(R.id.menu_create_dir);
         final MenuItem grid = menu.findItem(R.id.menu_grid);
         final MenuItem list = menu.findItem(R.id.menu_list);
-        final MenuItem advanced = menu.findItem(R.id.menu_advanced);
         final MenuItem fileSize = menu.findItem(R.id.menu_file_size);
         final MenuItem settings = menu.findItem(R.id.menu_settings);
 
-        boolean fileSizeVisible = mState.showSize && !mState.forceSize;
-        if (mState.action == ACTION_CREATE
+        boolean recents = cwd == null;
+        boolean picking = mState.action == ACTION_CREATE
                 || mState.action == ACTION_OPEN_TREE
-                || mState.action == ACTION_OPEN_COPY_DESTINATION) {
-            createDir.setVisible(cwd != null && cwd.isCreateSupported());
-            mSearchManager.showMenu(false);
+                || mState.action == ACTION_OPEN_COPY_DESTINATION;
 
-            // No display options in recent directories
-            if (cwd == null) {
-                grid.setVisible(false);
-                list.setVisible(false);
-                fileSizeVisible = false;
-            }
+        createDir.setVisible(picking && !recents && cwd.isCreateSupported());
+        mSearchManager.showMenu(!picking);
 
-            if (mState.action == ACTION_CREATE) {
-                final FragmentManager fm = getFragmentManager();
-                SaveFragment.get(fm).setSaveEnabled(cwd != null && cwd.isCreateSupported());
-            }
-        } else {
-            createDir.setVisible(false);
+        // No display options in recent directories
+        grid.setVisible(!(picking && recents));
+        list.setVisible(!(picking && recents));
+
+        fileSize.setVisible(fileSize.isVisible() && !picking);
+        settings.setVisible(false);
+
+        if (mState.action == ACTION_CREATE) {
+            final FragmentManager fm = getFragmentManager();
+            SaveFragment.get(fm).setSaveEnabled(cwd != null && cwd.isCreateSupported());
         }
 
-        advanced.setVisible(!mState.forceAdvanced);
-        fileSize.setVisible(fileSizeVisible);
-        settings.setVisible(false);
+        Menus.disableHiddenItems(menu);
 
         return true;
     }
@@ -510,7 +502,7 @@
         } else if (mState.action == ACTION_OPEN_COPY_DESTINATION) {
             // Picking a copy destination is only used internally by us, so we
             // don't need to extend permissions to the caller.
-            intent.putExtra(CopyService.EXTRA_STACK, (Parcelable) mState.stack);
+            intent.putExtra(Shared.EXTRA_STACK, (Parcelable) mState.stack);
             intent.putExtra(CopyService.EXTRA_TRANSFER_MODE, mState.transferMode);
         } else {
             intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION
@@ -611,7 +603,7 @@
             if (result != null) {
                 onTaskFinished(result);
             } else {
-                Shared.makeSnackbar(
+                Snackbars.makeSnackbar(
                     DocumentsActivity.this, R.string.save_error, Snackbar.LENGTH_SHORT).show();
             }
 
diff --git a/packages/DocumentsUI/src/com/android/documentsui/FailureDialogFragment.java b/packages/DocumentsUI/src/com/android/documentsui/FailureDialogFragment.java
index ea0c18a..120f610 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/FailureDialogFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/FailureDialogFragment.java
@@ -66,7 +66,7 @@
         if (whichButton == DialogInterface.BUTTON_POSITIVE) {
             CopyService.start(getActivity(), mFailedSrcList,
                     (DocumentStack) getActivity().getIntent().getParcelableExtra(
-                            CopyService.EXTRA_STACK),
+                            Shared.EXTRA_STACK),
                             mTransferMode);
         }
     }
diff --git a/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java b/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
index df803fb..1330b3c 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
@@ -30,6 +30,8 @@
 import android.content.Intent;
 import android.net.Uri;
 import android.os.Bundle;
+import android.os.Parcelable;
+import android.provider.DocumentsContract;
 import android.support.annotation.Nullable;
 import android.support.design.widget.Snackbar;
 import android.util.Log;
@@ -85,32 +87,51 @@
         mDrawer = DrawerController.create(this);
 
         RootsFragment.show(getFragmentManager(), null);
-        if (!mState.restored) {
-            Intent intent = getIntent();
-            Uri rootUri = intent.getData();
 
-            // If we've got a specific root to display, restore that root using a dedicated
-            // authority. That way a misbehaving provider won't result in an ANR.
-            if (rootUri != null && !LauncherActivity.isLaunchUri(rootUri)) {
-                new RestoreRootTask(rootUri).executeOnExecutor(
-                        ProviderExecutor.forAuthority(rootUri.getAuthority()));
+        if (mState.restored) {
+            if (DEBUG) Log.d(TAG, "Restored instance for uri: " + getIntent().getData());
+            onCurrentDirectoryChanged(ANIM_NONE);
+        } else {
+            Intent intent = getIntent();
+            Uri uri = intent.getData();
+
+            if (DEBUG) Log.d(TAG, "Creating new instance for uri: " + uri);
+            // If a non-empty stack is present in our state it was read (presumably)
+            // from EXTRA_STACK intent extra. In this case, we'll skip other means of
+            // loading or restoring the stack.
+            if (!mState.stack.isEmpty()) {
+                // When restoring from a stack, if a URI is present, it should only ever
+                // be a launch URI. Launch URIs support sensible activity management, but
+                // don't specify an real content target.
+                if (uri != null) {
+                    checkState(LauncherActivity.isLaunchUri(uri));
+                }
+                onCurrentDirectoryChanged(ANIM_NONE);
+            } else if (DocumentsContract.isRootUri(this, uri)) {
+                // If we've got a specific root to display, restore that root using a dedicated
+                // authority. That way a misbehaving provider won't result in an ANR.
+                new RestoreRootTask(uri).executeOnExecutor(
+                        ProviderExecutor.forAuthority(uri.getAuthority()));
             } else {
+                // Finally, we try to restore a stack from recents.
                 new RestoreStackTask().execute();
             }
 
+            // TODO: Ensure we're handling CopyService errors correctly across all activities.
             // Show a failure dialog if there was a failed operation.
-            final DocumentStack dstStack = intent.getParcelableExtra(CopyService.EXTRA_STACK);
             final int failure = intent.getIntExtra(CopyService.EXTRA_FAILURE, 0);
             final int transferMode = intent.getIntExtra(CopyService.EXTRA_TRANSFER_MODE,
                     CopyService.TRANSFER_MODE_NONE);
             if (failure != 0) {
                 final ArrayList<DocumentInfo> failedSrcList =
                         intent.getParcelableArrayListExtra(CopyService.EXTRA_SRC_LIST);
-                FailureDialogFragment.show(getFragmentManager(), failure, failedSrcList, dstStack,
+                FailureDialogFragment.show(
+                        getFragmentManager(),
+                        failure,
+                        failedSrcList,
+                        mState.stack,
                         transferMode);
             }
-        } else {
-            onCurrentDirectoryChanged(ANIM_NONE);
         }
     }
 
@@ -126,7 +147,7 @@
         // Options specific to the DocumentsActivity.
         checkArgument(!intent.hasExtra(Intent.EXTRA_LOCAL_ONLY));
 
-        final DocumentStack stack = intent.getParcelableExtra(CopyService.EXTRA_STACK);
+        final DocumentStack stack = intent.getParcelableExtra(Shared.EXTRA_STACK);
         if (stack != null) {
             state.stack = stack;
         }
@@ -206,30 +227,23 @@
 
     @Override
     public boolean onPrepareOptionsMenu(Menu menu) {
-        boolean shown = super.onPrepareOptionsMenu(menu);
-
-        menu.findItem(R.id.menu_file_size).setVisible(true);
-        menu.findItem(R.id.menu_advanced).setVisible(true);
+        super.onPrepareOptionsMenu(menu);
 
         final MenuItem createDir = menu.findItem(R.id.menu_create_dir);
         final MenuItem newWindow = menu.findItem(R.id.menu_new_window);
         final MenuItem pasteFromCb = menu.findItem(R.id.menu_paste_from_clipboard);
 
-        boolean canCreateDir = canCreateDirectory();
-
         createDir.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
-        createDir.setVisible(canCreateDir);
-        createDir.setEnabled(canCreateDir);
+        createDir.setVisible(true);
+        createDir.setEnabled(canCreateDirectory());
+
+        pasteFromCb.setEnabled(mClipper.hasItemsToPaste());
 
         newWindow.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
         newWindow.setVisible(mProductivityDevice);
-        newWindow.setEnabled(mProductivityDevice);
 
-        pasteFromCb.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
-        pasteFromCb.setVisible(true);
-        pasteFromCb.setEnabled(mClipper.hasItemsToPaste());
-
-        return shown;
+        Menus.disableHiddenItems(menu, pasteFromCb);
+        return true;
     }
 
     @Override
@@ -240,7 +254,7 @@
                 showCreateDirectoryDialog();
                 return true;
             case R.id.menu_new_window:
-                startActivity(LauncherActivity.createLaunchIntent(this));
+                createNewWindow();
                 return true;
             case R.id.menu_paste_from_clipboard:
                 DirectoryFragment dir = DirectoryFragment.get(getFragmentManager());
@@ -252,6 +266,12 @@
         return super.onOptionsItemSelected(item);
     }
 
+    private void createNewWindow() {
+        Intent intent = LauncherActivity.createLaunchIntent(this);
+        intent.putExtra(Shared.EXTRA_STACK, (Parcelable) mState.stack);
+        startActivity(intent);
+    }
+
     @Override
     void onDirectoryChanged(int anim) {
         final FragmentManager fm = getFragmentManager();
@@ -322,7 +342,7 @@
         try {
             startActivity(intent);
         } catch (ActivityNotFoundException ex2) {
-            Shared.makeSnackbar(this, R.string.toast_no_application, Snackbar.LENGTH_SHORT).show();
+            Snackbars.makeSnackbar(this, R.string.toast_no_application, Snackbar.LENGTH_SHORT).show();
         }
     }
 
diff --git a/packages/DocumentsUI/src/com/android/documentsui/FilteringCursorWrapper.java b/packages/DocumentsUI/src/com/android/documentsui/FilteringCursorWrapper.java
index 17a1161..7426af5 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/FilteringCursorWrapper.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/FilteringCursorWrapper.java
@@ -16,6 +16,7 @@
 
 package com.android.documentsui;
 
+import static com.android.documentsui.Shared.DEBUG;
 import static com.android.documentsui.Shared.TAG;
 import static com.android.documentsui.model.DocumentInfo.getCursorLong;
 import static com.android.documentsui.model.DocumentInfo.getCursorString;
@@ -65,7 +66,9 @@
             }
         }
 
-        Log.d(TAG, "Before filtering " + cursor.getCount() + ", after " + mCount);
+        if (DEBUG && mCount != cursor.getCount()) {
+            Log.d(TAG, "Before filtering " + cursor.getCount() + ", after " + mCount);
+        }
     }
 
     @Override
diff --git a/packages/DocumentsUI/src/com/android/documentsui/LauncherActivity.java b/packages/DocumentsUI/src/com/android/documentsui/LauncherActivity.java
index c29937d..b3d0cf3 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/LauncherActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/LauncherActivity.java
@@ -16,15 +16,17 @@
 
 package com.android.documentsui;
 
+import static com.android.documentsui.Shared.DEBUG;
+
 import android.app.Activity;
 import android.app.ActivityManager;
 import android.app.ActivityManager.AppTask;
-import android.app.ActivityManager.RecentTaskInfo;
 import android.content.Context;
 import android.content.Intent;
 import android.net.Uri;
 import android.os.Bundle;
 import android.support.annotation.Nullable;
+import android.util.Log;
 
 import java.util.List;
 
@@ -39,39 +41,47 @@
  */
 public class LauncherActivity extends Activity {
 
-    public static final String LAUNCH_CONTROL_AUTHORITY = "com.android.documentsui.launchControl";
+    private static final String LAUNCH_CONTROL_AUTHORITY = "com.android.documentsui.launchControl";
+    private static final String TAG = "LauncherActivity";
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
         ActivityManager activities = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
-        List<AppTask> tasks = activities.getAppTasks();
 
-        AppTask raiseTask = null;
-        for (AppTask task : tasks) {
-            Uri taskUri = task.getTaskInfo().baseIntent.getData();
-            if (taskUri != null && isLaunchUri(taskUri)) {
-                raiseTask = task;
-            }
-        }
-
-        if (raiseTask == null) {
-            launchFilesTask();
+        Intent intent = findTask(activities);
+        if (intent != null) {
+            restoreTask(intent);
         } else {
-            raiseFilesTask(activities, raiseTask.getTaskInfo());
+            startTask();
         }
 
         finish();
     }
 
-    private void launchFilesTask() {
+    private @Nullable Intent findTask(ActivityManager activities) {
+        List<AppTask> tasks = activities.getAppTasks();
+        for (AppTask task : tasks) {
+            Intent intent = task.getTaskInfo().baseIntent;
+            Uri uri = intent.getData();
+            if (isLaunchUri(uri)) {
+                return intent;
+            }
+        }
+        return null;
+    }
+
+    private void startTask() {
         Intent intent = createLaunchIntent(this);
+        if (DEBUG) Log.d(TAG, "Starting new task > " + intent.getData());
         startActivity(intent);
     }
 
-    private void raiseFilesTask(ActivityManager activities, RecentTaskInfo task) {
-        activities.moveTaskToFront(task.id, 0);
+    private void restoreTask(Intent intent) {
+        if (DEBUG) Log.d(TAG, "Restoring existing task > " + intent.getData());
+        // TODO: This doesn't appear to restore a task once it has stopped running.
+        startActivity(intent);
     }
 
     static Intent createLaunchIntent(Context context) {
@@ -88,6 +98,7 @@
     }
 
     static boolean isLaunchUri(@Nullable Uri uri) {
-        return uri != null && LAUNCH_CONTROL_AUTHORITY.equals(uri.getAuthority());
+        boolean result = uri != null && LAUNCH_CONTROL_AUTHORITY.equals(uri.getAuthority());
+        return result;
     }
 }
diff --git a/packages/DocumentsUI/src/com/android/documentsui/ManageRootActivity.java b/packages/DocumentsUI/src/com/android/documentsui/ManageRootActivity.java
index ed7333d..14a33f9 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/ManageRootActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/ManageRootActivity.java
@@ -140,6 +140,7 @@
     @Override
     public boolean onPrepareOptionsMenu(Menu menu) {
         super.onPrepareOptionsMenu(menu);
+        Menus.disableHiddenItems(menu);
         return true;
     }
 
@@ -184,7 +185,7 @@
                 try {
                     startActivity(view);
                 } catch (ActivityNotFoundException ex2) {
-                    Shared.makeSnackbar(this, R.string.toast_no_application, Snackbar.LENGTH_SHORT)
+                    Snackbars.makeSnackbar(this, R.string.toast_no_application, Snackbar.LENGTH_SHORT)
                             .show();
                 }
             }
diff --git a/packages/DocumentsUI/src/com/android/documentsui/Menus.java b/packages/DocumentsUI/src/com/android/documentsui/Menus.java
new file mode 100644
index 0000000..3f43a3d
--- /dev/null
+++ b/packages/DocumentsUI/src/com/android/documentsui/Menus.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.documentsui;
+
+import android.view.Menu;
+import android.view.MenuItem;
+
+final class Menus {
+
+    private Menus() {}
+
+    /**
+     * Disables hidden menu items so that they are not invokable via command shortcuts
+     */
+    static void disableHiddenItems(Menu menu, MenuItem... exclusions) {
+        for (int i = 0; i < menu.size(); i++) {
+            MenuItem item = menu.getItem(i);
+            if (item.isVisible()) {
+              continue;
+            }
+            if (contains(exclusions, item)) {
+                continue;
+            }
+            item.setEnabled(false);
+        }
+    }
+
+    private static boolean contains(MenuItem[] exclusions, MenuItem item) {
+        for (int x = 0; x < exclusions.length; x++) {
+            if (exclusions[x] == item) {
+                return true;
+            }
+        }
+        return false;
+    }
+}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/Shared.java b/packages/DocumentsUI/src/com/android/documentsui/Shared.java
index 29bcd24..a4d6dc5 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/Shared.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/Shared.java
@@ -16,19 +16,13 @@
 
 package com.android.documentsui;
 
-import static com.android.internal.util.Preconditions.checkNotNull;
-
-import android.app.Activity;
 import android.content.Context;
-import android.support.design.widget.Snackbar;
-import android.view.View;
 
-/**
- * @hide
- */
+/** @hide */
 public final class Shared {
     public static final boolean DEBUG = true;
     public static final String TAG = "Documents";
+    public static final String EXTRA_STACK = "com.android.documentsui.STACK";
 
     /**
      * Generates a formatted quantity string.
@@ -36,14 +30,4 @@
     public static final String getQuantityString(Context context, int resourceId, int quantity) {
         return context.getResources().getQuantityString(resourceId, quantity, quantity);
     }
-
-    public static final Snackbar makeSnackbar(Activity activity, int messageId, int duration) {
-        return makeSnackbar(activity, activity.getResources().getText(messageId), duration);
-    }
-
-    public static final Snackbar makeSnackbar(Activity activity, CharSequence message, int duration)
-    {
-        final View view = checkNotNull(activity.findViewById(R.id.coordinator_layout));
-        return Snackbar.make(view, message, duration);
-    }
 }
diff --git a/packages/DocumentsUI/src/com/android/documentsui/Snackbars.java b/packages/DocumentsUI/src/com/android/documentsui/Snackbars.java
new file mode 100644
index 0000000..f48b298
--- /dev/null
+++ b/packages/DocumentsUI/src/com/android/documentsui/Snackbars.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.documentsui;
+
+import static com.android.internal.util.Preconditions.checkNotNull;
+
+import android.app.Activity;
+import android.support.design.widget.Snackbar;
+import android.view.View;
+
+final class Snackbars {
+    private Snackbars() {}
+
+    public static final Snackbar makeSnackbar(Activity activity, int messageId, int duration) {
+        return Snackbars.makeSnackbar(activity, activity.getResources().getText(messageId), duration);
+    }
+
+    public static final Snackbar makeSnackbar(Activity activity, CharSequence message, int duration)
+    {
+        final View view = checkNotNull(activity.findViewById(R.id.coordinator_layout));
+        return Snackbar.make(view, message, duration);
+    }
+}
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/CopyTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/CopyTest.java
index 568e9e4..fc42c3b 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/CopyTest.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/CopyTest.java
@@ -425,7 +425,7 @@
         stack.push(DocumentInfo.fromUri(mResolver, dst));
         final Intent copyIntent = new Intent(mContext, CopyService.class);
         copyIntent.putParcelableArrayListExtra(CopyService.EXTRA_SRC_LIST, srcDocs);
-        copyIntent.putExtra(CopyService.EXTRA_STACK, (Parcelable) stack);
+        copyIntent.putExtra(Shared.EXTRA_STACK, (Parcelable) stack);
 
         // startService(copyIntent);
         return copyIntent;
diff --git a/packages/SystemUI/proguard.flags b/packages/SystemUI/proguard.flags
index 47e24e8..368f9f7 100644
--- a/packages/SystemUI/proguard.flags
+++ b/packages/SystemUI/proguard.flags
@@ -13,3 +13,11 @@
 -keep class com.android.systemui.statusbar.phone.PhoneStatusBar
 -keep class com.android.systemui.statusbar.tv.TvStatusBar
 -keep class com.android.systemui.recents.*
+
+-keepclassmembers class ** {
+    public void onBusEvent(**);
+    public void onInterprocessBusEvent(**);
+}
+-keepclassmembers class ** extends **.EventBus$InterprocessEvent {
+	public <init>(android.os.Bundle);
+}
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/qs_customizer_background.xml b/packages/SystemUI/res/drawable/qs_customizer_background.xml
new file mode 100644
index 0000000..6bb27cc
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_customizer_background.xml
@@ -0,0 +1,19 @@
+<!--
+    Copyright (C) 2015 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<transition xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:drawable="@color/qs_detail_transition" />
+    <item android:drawable="?android:attr/windowBackground" />
+</transition>
diff --git a/packages/SystemUI/res/layout/qs_customize_panel.xml b/packages/SystemUI/res/layout/qs_customize_panel.xml
index dc928c7..59fed5b 100644
--- a/packages/SystemUI/res/layout/qs_customize_panel.xml
+++ b/packages/SystemUI/res/layout/qs_customize_panel.xml
@@ -20,7 +20,8 @@
     android:layout_height="match_parent"
     android:orientation="vertical"
     android:paddingBottom="@dimen/navigation_bar_size"
-    android:background="?android:attr/windowBackground">
+    android:background="@drawable/qs_customizer_background"
+    android:gravity="center_horizontal">
 
     <FrameLayout
         android:layout_width="match_parent"
@@ -76,10 +77,10 @@
     </FrameLayout>
 
     <com.android.systemui.tuner.AutoScrollView
-        android:layout_width="match_parent"
+        android:layout_width="@dimen/notification_panel_width"
         android:layout_height="0dp"
         android:layout_weight="1"
-        android:paddingTop="8dp"
+        android:paddingTop="12dp"
         android:paddingBottom="8dp"
         android:elevation="2dp">
 
@@ -87,7 +88,9 @@
             android:id="@+id/quick_settings_panel"
             android:background="#0000"
             android:layout_width="match_parent"
-            android:layout_height="wrap_content" />
+            android:layout_height="wrap_content"
+            android:layout_marginLeft="@dimen/notification_side_padding"
+            android:layout_marginRight="@dimen/notification_side_padding" />
 
     </com.android.systemui.tuner.AutoScrollView>
 
diff --git a/packages/SystemUI/res/values-hy-rAM/strings.xml b/packages/SystemUI/res/values-hy-rAM/strings.xml
index f8fc232..b69fc0f 100644
--- a/packages/SystemUI/res/values-hy-rAM/strings.xml
+++ b/packages/SystemUI/res/values-hy-rAM/strings.xml
@@ -325,8 +325,8 @@
     <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Միայն\nկարևորները"</string>
     <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Միայն\nզարթուցիչ"</string>
     <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Լիցքավորում (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> մինչև լրիվ լիցքավորումը)"</string>
-    <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Արագ լիցքավորում (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>՝ մինչև լցվելը)"</string>
-    <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Դանդաղ լիցքավորում (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>՝ մինչև լցվելը)"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Արագ լիցքավորում (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>՝ մինչև ավարտ)"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Դանդաղ լիցքավորում (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>՝ մինչև ավարտ)"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Անջատել օգտվողին"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Փոխել օգտվողին. ներկայիս օգտվողն է՝ <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Ընթացիկ օգտվողը՝ <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index d91335c..7323126 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -393,7 +393,7 @@
     <string name="accessibility_volume_expand" msgid="5946812790999244205">"Utöka"</string>
     <string name="accessibility_volume_collapse" msgid="3609549593031810875">"Komprimera"</string>
     <string name="screen_pinning_title" msgid="3273740381976175811">"Skärmen har fästs"</string>
-    <string name="screen_pinning_description" msgid="1346522416878235405">"Detta visar skärmen tills du lossar den. Tryck länge på bakåtknappen och Översikt samtidigt om du vill lossa skärmen."</string>
+    <string name="screen_pinning_description" msgid="1346522416878235405">"Med den här funktionen är skärmen synlig tills du lossar den. Tryck länge på Tillbaka och Översikt samtidigt om du vill lossa skärmen."</string>
     <string name="screen_pinning_description_accessible" msgid="8518446209564202557">"Detta visar skärmen tills du lossar den. Tryck länge på Översikt om du vill lossa skärmen."</string>
     <string name="screen_pinning_positive" msgid="3783985798366751226">"OK"</string>
     <string name="screen_pinning_negative" msgid="3741602308343880268">"Nej tack"</string>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 1d19589..bae8017 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -222,7 +222,7 @@
     <bool name="doze_pulse_on_notifications">true</bool>
 
     <!-- Doze: when to pulse after a buzzworthy notification arrives -->
-    <string name="doze_pulse_schedule" translatable="false">1s,10s,30s,60s</string>
+    <string name="doze_pulse_schedule" translatable="false">10s,30s,60s</string>
 
     <!-- Doze: maximum number of times the notification pulse schedule can be reset -->
     <integer name="doze_pulse_schedule_resets">2</integer>
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
index 36efead..3370091 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
@@ -88,6 +88,7 @@
     private boolean mPowerSaveActive;
     private boolean mCarMode;
     private long mNotificationPulseTime;
+    private long mLastScheduleResetTime;
     private long mEarliestPulseDueToLight;
     private int mScheduleResetsRemaining;
 
@@ -356,13 +357,21 @@
             return;
         }
         final long pulseDuration = mDozeParameters.getPulseDuration(false /*pickup*/);
-        if ((notificationTimeMs - mNotificationPulseTime) < pulseDuration) {
+        boolean pulseImmediately = System.currentTimeMillis() >= notificationTimeMs;
+        if ((notificationTimeMs - mLastScheduleResetTime) >= pulseDuration) {
+            mScheduleResetsRemaining--;
+            mLastScheduleResetTime = notificationTimeMs;
+        } else if (!pulseImmediately){
             if (DEBUG) Log.d(mTag, "Recently updated, not resetting schedule");
             return;
         }
-        mScheduleResetsRemaining--;
         if (DEBUG) Log.d(mTag, "mScheduleResetsRemaining = " + mScheduleResetsRemaining);
         mNotificationPulseTime = notificationTimeMs;
+        if (pulseImmediately) {
+            DozeLog.traceNotificationPulse(0);
+            requestPulse(DozeLog.PULSE_REASON_NOTIFICATION);
+        }
+        // schedule the rest of the pulses
         rescheduleNotificationPulse(true /*predicate*/);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index c612600..32c906e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -181,6 +181,7 @@
         public TilePage(Context context, AttributeSet attrs) {
             super(context, attrs);
             mAllowDual = false;
+            updateResources();
         }
 
         public void setMaxRows(int maxRows) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
index ca38528..47189b0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
@@ -140,13 +140,13 @@
         mDialog.setTitle(getTitle(deviceOwner));
         mDialog.setMessage(getMessage(deviceOwner, profileOwner, primaryVpn, profileVpn, managed));
         mDialog.setButton(DialogInterface.BUTTON_POSITIVE, getPositiveButton(), this);
-        if (mSecurityController.isVpnEnabled()) {
-            mDialog.setButton(DialogInterface.BUTTON_NEGATIVE, getNegativeButton(), this);
+        if (mSecurityController.isVpnEnabled() && !mSecurityController.isVpnRestricted()) {
+            mDialog.setButton(DialogInterface.BUTTON_NEGATIVE, getSettingsButton(), this);
         }
         mDialog.show();
     }
 
-    private String getNegativeButton() {
+    private String getSettingsButton() {
         return mContext.getString(R.string.status_bar_settings_settings_button);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 880349e..18af35e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -172,7 +172,8 @@
                 mCustomizePanel.setHost(mHost);
             } else {
                 if (mCustomizePanel != null && mCustomizePanel.isCustomizing()) {
-                    mCustomizePanel.hide();
+                    mCustomizePanel.hide(mCustomizePanel.getWidth() / 2,
+                            mCustomizePanel.getHeight() / 2);
                 }
                 mCustomizePanel = null;
             }
@@ -242,7 +243,7 @@
 
     public void onCollapse() {
         if (mCustomizePanel != null && mCustomizePanel.isCustomizing()) {
-            mCustomizePanel.hide();
+            mCustomizePanel.hide(mCustomizePanel.getWidth() / 2, mCustomizePanel.getHeight() / 2);
         }
     }
 
@@ -382,7 +383,12 @@
             public boolean onLongClick(View v) {
                 if (mCustomizePanel != null) {
                     if (!mCustomizePanel.isCustomizing()) {
-                        mCustomizePanel.show();
+                        int[] loc = new int[2];
+                        getLocationInWindow(loc);
+                        int x = r.tileView.getLeft() + r.tileView.getWidth() / 2 + loc[0];
+                        int y = r.tileView.getTop() + mTileLayout.getOffsetTop(r)
+                                + r.tileView.getHeight() / 2 + loc[1];
+                        mCustomizePanel.show(x, y);
                     }
                 } else {
                     r.tile.longClick();
@@ -409,7 +415,7 @@
     public void closeDetail() {
         if (mCustomizePanel != null && mCustomizePanel.isCustomizing()) {
             // Treat this as a detail panel for now, to make things easy.
-            mCustomizePanel.hide();
+            mCustomizePanel.hide(mCustomizePanel.getWidth() / 2, mCustomizePanel.getHeight() / 2);
             return;
         }
         showDetail(false, mDetailRecord);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
index 8bd05fa..b8342e2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
@@ -68,9 +68,10 @@
         final Resources res = mContext.getResources();
         final int columns = Math.max(1, res.getInteger(R.integer.quick_settings_num_columns));
         mCellHeight = res.getDimensionPixelSize(R.dimen.qs_tile_height);
-        mCellWidth = (int)(mCellHeight * TILE_ASPECT);
-        mLargeCellHeight = res.getDimensionPixelSize(R.dimen.qs_dual_tile_height);
-        mLargeCellWidth = (int)(mLargeCellHeight * TILE_ASPECT);
+        mCellWidth = (int) (mCellHeight * TILE_ASPECT);
+        mLargeCellHeight = mAllowDual ? res.getDimensionPixelSize(R.dimen.qs_dual_tile_height)
+                : mCellHeight;
+        mLargeCellWidth = mAllowDual ? (int) (mLargeCellHeight * TILE_ASPECT) : mCellWidth;
         mDualTileUnderlap = res.getDimensionPixelSize(R.dimen.qs_dual_tile_padding_vertical);
         if (mColumns != columns) {
             mColumns = columns;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/CustomQSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/customize/CustomQSTileHost.java
index f676ea3..3491cb6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/CustomQSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/CustomQSTileHost.java
@@ -160,6 +160,11 @@
         }
 
         @Override
+        public boolean isVpnRestricted() {
+            return false;
+        }
+
+        @Override
         public String getPrimaryVpnName() {
             return null;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
index 7e74785..601961b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
@@ -15,23 +15,19 @@
  */
 package com.android.systemui.qs.customize;
 
+import android.animation.Animator;
 import android.content.ClipData;
 import android.content.Context;
 import android.util.AttributeSet;
 import android.util.TypedValue;
-import android.view.ContextThemeWrapper;
-import android.view.DragEvent;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.View;
+import android.view.*;
 import android.view.View.OnClickListener;
-import android.view.ViewGroup;
 import android.widget.LinearLayout;
 import android.widget.Toolbar;
 import android.widget.Toolbar.OnMenuItemClickListener;
-
 import com.android.systemui.R;
 import com.android.systemui.SystemUIApplication;
+import com.android.systemui.qs.QSDetailClipper;
 import com.android.systemui.qs.QSTile.Host.Callback;
 import com.android.systemui.qs.customize.DropButton.OnDropListener;
 import com.android.systemui.statusbar.phone.PhoneStatusBar;
@@ -48,10 +44,11 @@
  * *someday* do fancy animations to get into/out of it.
  */
 public class QSCustomizer extends LinearLayout implements OnMenuItemClickListener, Callback,
-        OnDropListener, OnClickListener {
+        OnDropListener, OnClickListener, Animator.AnimatorListener {
 
     private static final int MENU_SAVE = Menu.FIRST;
     private static final int MENU_RESET = Menu.FIRST + 1;
+    private final QSDetailClipper mClipper;
 
     private PhoneStatusBar mPhoneStatusBar;
 
@@ -69,6 +66,7 @@
         super(new ContextThemeWrapper(context, android.R.style.Theme_Material), attrs);
         mPhoneStatusBar = ((SystemUIApplication) mContext.getApplicationContext())
                 .getComponent(PhoneStatusBar.class);
+        mClipper = new QSDetailClipper(this);
     }
 
     public void setHost(QSTileHost host) {
@@ -90,8 +88,7 @@
         mToolbar.setNavigationOnClickListener(new OnClickListener() {
             @Override
             public void onClick(View v) {
-                // TODO: Is this all we want...?
-                hide();
+                hide(0, 0);
             }
         });
         mToolbar.setOnMenuItemClickListener(this);
@@ -115,17 +112,18 @@
         mFab.setOnClickListener(this);
     }
 
-    public void show() {
+    public void show(int x, int y) {
         isShown = true;
         mHost.setSavedTiles();
-        // TODO: Fancy shmancy reveal.
         mPhoneStatusBar.getStatusBarWindow().addView(this);
+        mQsPanel.setListening(true);
+        mClipper.animateCircularClip(x, y, true, this);
     }
 
-    public void hide() {
+    public void hide(int x, int y) {
         isShown = false;
-        // TODO: Similarly awesome or better hide.
-        mPhoneStatusBar.getStatusBarWindow().removeView(this);
+        mQsPanel.setListening(false);
+        mClipper.animateCircularClip(x, y, false, this);
     }
 
     public boolean isCustomizing() {
@@ -146,7 +144,8 @@
 
     private void save() {
         mHost.saveCurrentTiles();
-        hide();
+        // TODO: At save button.
+        hide(0, 0);
     }
 
     @Override
@@ -197,4 +196,28 @@
             // TODO: Show list of tiles.
         }
     }
+
+    @Override
+    public void onAnimationEnd(Animator animation) {
+        if (!isShown) {
+            mPhoneStatusBar.getStatusBarWindow().removeView(this);
+        }
+    }
+
+    @Override
+    public void onAnimationCancel(Animator animation) {
+        if (!isShown) {
+            mPhoneStatusBar.getStatusBarWindow().removeView(this);
+        }
+    }
+
+    @Override
+    public void onAnimationStart(Animator animation) {
+        // Don't care.
+    }
+
+    @Override
+    public void onAnimationRepeat(Animator animation) {
+        // Don't care.
+    }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index 23cc8f0..b1cc27a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -74,6 +74,8 @@
 public class Recents extends SystemUI
         implements ActivityOptions.OnAnimationStartedListener, RecentsComponent {
 
+    public final static int EVENT_BUS_PRIORITY = 1;
+
     final public static String EXTRA_TRIGGERED_FROM_ALT_TAB = "triggeredFromAltTab";
     final public static String EXTRA_TRIGGERED_FROM_HOME_KEY = "triggeredFromHomeKey";
     final public static String EXTRA_RECENTS_VISIBILITY = "recentsVisibility";
@@ -114,9 +116,8 @@
 
         /** Preloads the next task */
         public void run() {
-            // Temporarily skip this if multi stack is enabled
-            if (mConfig.multiWindowEnabled) return;
-
+            // TODO: Temporarily skip this if multi stack is enabled
+            /*
             RecentsConfiguration config = RecentsConfiguration.getInstance();
             if (config.svelteLevel == RecentsConfiguration.SVELTE_NONE) {
                 RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
@@ -125,7 +126,7 @@
 
                 // Load the next task only if we aren't svelte
                 RecentsTaskLoadPlan plan = loader.createLoadPlan(mContext);
-                loader.preloadTasks(plan, true /* isTopTaskHome */);
+                loader.preloadTasks(plan, true);
                 RecentsTaskLoadPlan.Options launchOpts = new RecentsTaskLoadPlan.Options();
                 // This callback is made when a new activity is launched and the old one is paused
                 // so ignore the current activity and try and preload the thumbnail for the
@@ -139,6 +140,7 @@
                 launchOpts.onlyLoadPausedActivities = true;
                 loader.loadTasks(mContext, plan, launchOpts);
             }
+            */
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index 9ce6b2c..e647c1f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -19,23 +19,35 @@
 import android.app.Activity;
 import android.app.ActivityOptions;
 import android.app.SearchManager;
+import android.app.TaskStackBuilder;
 import android.appwidget.AppWidgetManager;
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.ActivityInfo;
+import android.net.Uri;
 import android.os.Bundle;
 import android.os.SystemClock;
 import android.os.UserHandle;
+import android.provider.Settings;
 import android.view.KeyEvent;
 import android.view.View;
 import android.view.ViewStub;
 import com.android.internal.logging.MetricsLogger;
 import com.android.systemui.R;
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.events.activity.AppWidgetProviderChangedEvent;
+import com.android.systemui.recents.events.ui.DismissTaskEvent;
+import com.android.systemui.recents.events.ui.ResizeTaskEvent;
+import com.android.systemui.recents.events.ui.ShowApplicationInfoEvent;
+import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent;
+import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent;
 import com.android.systemui.recents.misc.Console;
 import com.android.systemui.recents.misc.ReferenceCountedTrigger;
 import com.android.systemui.recents.misc.SystemServicesProxy;
+import com.android.systemui.recents.model.RecentsPackageMonitor;
 import com.android.systemui.recents.model.RecentsTaskLoadPlan;
 import com.android.systemui.recents.model.RecentsTaskLoader;
 import com.android.systemui.recents.model.Task;
@@ -44,16 +56,17 @@
 import com.android.systemui.recents.views.SystemBarScrimViews;
 import com.android.systemui.recents.views.ViewAnimation;
 
-import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 
 /**
  * The main Recents activity that is started from AlternateRecentsComponent.
  */
-public class RecentsActivity extends Activity implements RecentsView.RecentsViewCallbacks,
-        RecentsAppWidgetHost.RecentsAppWidgetHostCallbacks {
+public class RecentsActivity extends Activity implements RecentsView.RecentsViewCallbacks {
+
+    public final static int EVENT_BUS_PRIORITY = Recents.EVENT_BUS_PRIORITY + 1;
 
     RecentsConfiguration mConfig;
+    RecentsPackageMonitor mPackageMonitor;
     long mLastTabKeyEventTime;
 
     // Top level views
@@ -98,22 +111,11 @@
 
         @Override
         public void run() {
-            // Finish Recents
-            if (mLaunchIntent != null) {
-                try {
-                    if (mLaunchOpts != null) {
-                        startActivityAsUser(mLaunchIntent, mLaunchOpts.toBundle(), UserHandle.CURRENT);
-                    } else {
-                        startActivityAsUser(mLaunchIntent, UserHandle.CURRENT);
-                    }
-                } catch (Exception e) {
-                    Console.logError(RecentsActivity.this,
-                            getString(R.string.recents_launch_error_message, "Home"));
-                }
-            } else {
-                finish();
-                overridePendingTransition(R.anim.recents_to_launcher_enter,
-                        R.anim.recents_to_launcher_exit);
+            try {
+                startActivityAsUser(mLaunchIntent, mLaunchOpts.toBundle(), UserHandle.CURRENT);
+            } catch (Exception e) {
+                Console.logError(RecentsActivity.this,
+                        getString(R.string.recents_launch_error_message, "Home"));
             }
         }
     }
@@ -131,7 +133,7 @@
                     dismissRecentsToFocusedTaskOrHome(false);
                 } else if (intent.getBooleanExtra(Recents.EXTRA_TRIGGERED_FROM_HOME_KEY, false)) {
                     // Otherwise, dismiss Recents to Home
-                    dismissRecentsToHomeRaw(true);
+                    dismissRecentsToHome(true);
                 } else {
                     // Do nothing
                 }
@@ -157,7 +159,7 @@
             String action = intent.getAction();
             if (action.equals(Intent.ACTION_SCREEN_OFF)) {
                 // When the screen turns off, dismiss Recents to Home
-                dismissRecentsToHome(false);
+                dismissRecentsToHomeIfVisible(false);
             } else if (action.equals(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED)) {
                 // When the search activity changes, update the search widget view
                 SystemServicesProxy ssp = RecentsTaskLoader.getInstance().getSystemServicesProxy();
@@ -273,25 +275,28 @@
             if (mRecentsView.launchFocusedTask()) return true;
             // If we launched from Home, then return to Home
             if (launchState.launchedFromHome) {
-                dismissRecentsToHomeRaw(true);
+                dismissRecentsToHome(true);
                 return true;
             }
             // Otherwise, try and return to the Task that Recents was launched from
             if (mRecentsView.launchPreviousTask()) return true;
             // If none of the other cases apply, then just go Home
-            dismissRecentsToHomeRaw(true);
+            dismissRecentsToHome(true);
             return true;
         }
         return false;
     }
 
-    /** Dismisses Recents directly to Home. */
-    void dismissRecentsToHomeRaw(boolean animated) {
+    /**
+     * Dismisses Recents directly to Home without checking whether it is currently visible.
+     */
+    void dismissRecentsToHome(boolean animated) {
         if (animated) {
             ReferenceCountedTrigger exitTrigger = new ReferenceCountedTrigger(this,
                     null, mFinishLaunchHomeRunnable, null);
             mRecentsView.startExitToHomeAnimation(
                     new ViewAnimation.TaskViewExitContext(exitTrigger));
+            mScrimViews.startExitRecentsAnimation();
         } else {
             mFinishLaunchHomeRunnable.run();
         }
@@ -304,11 +309,11 @@
     }
 
     /** Dismisses Recents directly to Home if we currently aren't transitioning. */
-    boolean dismissRecentsToHome(boolean animated) {
+    boolean dismissRecentsToHomeIfVisible(boolean animated) {
         SystemServicesProxy ssp = RecentsTaskLoader.getInstance().getSystemServicesProxy();
         if (ssp.isRecentsTopMost(ssp.getTopMostTask(), null)) {
             // Return to Home
-            dismissRecentsToHomeRaw(animated);
+            dismissRecentsToHome(animated);
             return true;
         }
         return false;
@@ -318,12 +323,17 @@
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
+
+        // Register this activity with the event bus
+        EventBus.getDefault().register(this, EVENT_BUS_PRIORITY);
+
         // For the non-primary user, ensure that the SystemServicesProxy and configuration is
         // initialized
         RecentsTaskLoader.initialize(this);
         SystemServicesProxy ssp = RecentsTaskLoader.getInstance().getSystemServicesProxy();
         mConfig = RecentsConfiguration.initialize(this, ssp);
         mConfig.update(this, ssp, ssp.getWindowRect());
+        mPackageMonitor = new RecentsPackageMonitor();
 
         // Initialize the widget host (the host id is static and does not change)
         mAppWidgetHost = new RecentsAppWidgetHost(this, Constants.Values.App.AppWidgetHostId);
@@ -371,7 +381,7 @@
         registerReceiver(mServiceBroadcastReceiver, filter);
 
         // Register any broadcast receivers for the task loader
-        loader.registerReceivers(this, mRecentsView);
+        mPackageMonitor.register(this);
 
         // Update the recent tasks
         updateRecentsTasks();
@@ -403,7 +413,6 @@
     protected void onStop() {
         super.onStop();
         MetricsLogger.hidden(this, MetricsLogger.OVERVIEW_ACTIVITY);
-        RecentsActivityLaunchState launchState = mConfig.getLaunchState();
         RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
         SystemServicesProxy ssp = loader.getSystemServicesProxy();
         Recents.notifyVisibilityChanged(this, ssp, false);
@@ -415,11 +424,12 @@
         unregisterReceiver(mServiceBroadcastReceiver);
 
         // Unregister any broadcast receivers for the task loader
-        loader.unregisterReceivers();
+        mPackageMonitor.unregister();
 
         // Workaround for b/22542869, if the RecentsActivity is started again, but without going
         // through SystemUI, we need to reset the config launch flags to ensure that we do not
         // wait on the system to send a signal that was never queued.
+        RecentsActivityLaunchState launchState = mConfig.getLaunchState();
         launchState.launchedFromHome = false;
         launchState.launchedFromSearchHome = false;
         launchState.launchedFromAppWithThumbnail = false;
@@ -437,6 +447,7 @@
 
         // Stop listening for widget package changes if there was one bound
         mAppWidgetHost.stopListening();
+        EventBus.getDefault().unregister(this);
     }
 
     public void onEnterAnimationTriggered() {
@@ -446,16 +457,12 @@
         mRecentsView.startEnterRecentsAnimation(ctx);
 
         if (mSearchWidgetInfo != null) {
-            final WeakReference<RecentsAppWidgetHost.RecentsAppWidgetHostCallbacks> cbRef =
-                    new WeakReference<RecentsAppWidgetHost.RecentsAppWidgetHostCallbacks>(
-                            RecentsActivity.this);
             ctx.postAnimationTrigger.addLastDecrementRunnable(new Runnable() {
                 @Override
                 public void run() {
                     // Start listening for widget package changes if there is one bound
-                    RecentsAppWidgetHost.RecentsAppWidgetHostCallbacks cb = cbRef.get();
-                    if (cb != null) {
-                        mAppWidgetHost.startListening(cb);
+                    if (mAppWidgetHost != null) {
+                        mAppWidgetHost.startListening();
                     }
                 }
             });
@@ -530,11 +537,6 @@
         return mResizeTaskDebugDialog;
     }
 
-    @Override
-    public void onTaskResize(Task t) {
-        getResizeTaskDebugDialog().showResizeTaskDialog(t, mRecentsView);
-    }
-
     /**** RecentsView.RecentsViewCallbacks Implementation ****/
 
     @Override
@@ -550,7 +552,7 @@
     @Override
     public void onTaskLaunchFailed() {
         // Return to Home
-        dismissRecentsToHomeRaw(true);
+        dismissRecentsToHome(true);
     }
 
     @Override
@@ -572,10 +574,52 @@
         mAfterPauseRunnable = r;
     }
 
-    /**** RecentsAppWidgetHost.RecentsAppWidgetHostCallbacks Implementation ****/
+    /**** EventBus events ****/
 
-    @Override
-    public void refreshSearchWidgetView() {
+    public final void onBusEvent(AppWidgetProviderChangedEvent event) {
+        refreshSearchWidgetView();
+    }
+
+    public final void onBusEvent(ShowApplicationInfoEvent event) {
+        // Create a new task stack with the application info details activity
+        Intent baseIntent = event.task.key.baseIntent;
+        Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
+                Uri.fromParts("package", baseIntent.getComponent().getPackageName(), null));
+        intent.setComponent(intent.resolveActivity(getPackageManager()));
+        TaskStackBuilder.create(this)
+                .addNextIntentWithParentStack(intent).startActivities(null,
+                new UserHandle(event.task.key.userId));
+
+        // Keep track of app-info invocations
+        MetricsLogger.count(this, "overview_app_info", 1);
+    }
+
+    public final void onBusEvent(DismissTaskEvent event) {
+        // Remove any stored data from the loader
+        RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
+        loader.deleteTaskData(event.task, false);
+
+        // Remove the task from activity manager
+        loader.getSystemServicesProxy().removeTask(event.task.key.id);
+    }
+
+    public final void onBusEvent(ResizeTaskEvent event) {
+        getResizeTaskDebugDialog().showResizeTaskDialog(event.task, mRecentsView);
+    }
+
+    public final void onBusEvent(DragStartEvent event) {
+        // Lock the orientation while dragging
+        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED);
+
+        // TODO: docking requires custom accessibility actions
+    }
+
+    public final void onBusEvent(DragEndEvent event) {
+        // Unlock the orientation when dragging completes
+        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_BEHIND);
+    }
+
+    private void refreshSearchWidgetView() {
         if (mSearchWidgetInfo != null) {
             SystemServicesProxy ssp = RecentsTaskLoader.getInstance().getSystemServicesProxy();
             int searchWidgetId = ssp.getSearchAppWidgetId(this);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsAppWidgetHost.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsAppWidgetHost.java
index 0102332..fc96c11 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsAppWidgetHost.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsAppWidgetHost.java
@@ -20,24 +20,19 @@
 import android.appwidget.AppWidgetHostView;
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.Context;
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.events.activity.AppWidgetProviderChangedEvent;
 
 /** Our special app widget host for the Search widget */
 public class RecentsAppWidgetHost extends AppWidgetHost {
 
-    /* Callbacks to notify when an app package changes */
-    interface RecentsAppWidgetHostCallbacks {
-        void refreshSearchWidgetView();
-    }
-
-    RecentsAppWidgetHostCallbacks mCb;
     boolean mIsListening;
 
     public RecentsAppWidgetHost(Context context, int hostId) {
         super(context, hostId);
     }
 
-    public void startListening(RecentsAppWidgetHostCallbacks cb) {
-        mCb = cb;
+    public void startListening() {
         if (!mIsListening) {
             mIsListening = true;
             super.startListening();
@@ -47,11 +42,9 @@
     @Override
     public void stopListening() {
         if (mIsListening) {
+            mIsListening = false;
             super.stopListening();
         }
-        // Ensure that we release any references to the callbacks
-        mCb = null;
-        mIsListening = false;
     }
 
     @Override
@@ -66,8 +59,8 @@
     @Override
     protected void onProviderChanged(int appWidgetId, AppWidgetProviderInfo appWidgetInfo) {
         super.onProviderChanged(appWidgetId, appWidgetInfo);
-        if (mIsListening && mCb != null) {
-            mCb.refreshSearchWidgetView();
+        if (mIsListening) {
+            EventBus.getDefault().send(new AppWidgetProviderChangedEvent());
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
index 52b9521..b6f4a3c 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
@@ -22,6 +22,7 @@
 import android.provider.Settings;
 import com.android.systemui.R;
 import com.android.systemui.recents.misc.SystemServicesProxy;
+import com.android.systemui.recents.model.RecentsTaskLoader;
 
 /**
  * Application resources that can be retrieved from the application context and are not specifically
@@ -70,13 +71,13 @@
     public final int smallestWidth;
 
     /** Misc **/
+    public boolean hasDockedTasks;
     public boolean useHardwareLayers;
     public boolean fakeShadows;
     public int svelteLevel;
     public int searchBarSpaceHeightPx;
 
     /** Dev options and global settings */
-    public boolean multiWindowEnabled;
     public boolean lockToAppEnabled;
 
     /** Private constructor */
@@ -113,7 +114,7 @@
         // settings or via multi window
         lockToAppEnabled = ssp.getSystemSetting(context,
                 Settings.System.LOCK_TO_APP_ENABLED) != 0;
-        multiWindowEnabled = "true".equals(ssp.getSystemProperty("persist.sys.debug.multi_window"));
+        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/RecentsResizeTaskDialog.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsResizeTaskDialog.java
index 59df293..8827065 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsResizeTaskDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsResizeTaskDialog.java
@@ -235,11 +235,9 @@
 
         // In debug mode, we force all task to be resizeable regardless of the
         // current app configuration.
-        if (RecentsConfiguration.getInstance().multiWindowEnabled) {
-            for (int i = additionalTasks; i >= 0; --i) {
-                if (mTasks[i] != null) {
-                    mSsp.setTaskResizeable(mTasks[i].key.id);
-                }
+        for (int i = additionalTasks; i >= 0; --i) {
+            if (mTasks[i] != null) {
+                mSsp.setTaskResizeable(mTasks[i].key.id);
             }
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/EventBus.java b/packages/SystemUI/src/com/android/systemui/recents/events/EventBus.java
new file mode 100644
index 0000000..ef543d0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/EventBus.java
@@ -0,0 +1,844 @@
+/*
+ * 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.recents.events;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.util.Log;
+import android.util.MutableBoolean;
+
+import java.lang.ref.WeakReference;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * Represents a subscriber, which implements various event bus handler methods.
+ */
+class Subscriber {
+    private WeakReference<Object> mSubscriber;
+
+    long registrationTime;
+
+    Subscriber(Object subscriber, long registrationTime) {
+        mSubscriber = new WeakReference<>(subscriber);
+        this.registrationTime = registrationTime;
+    }
+
+    public String toString(int priority) {
+        Object sub = mSubscriber.get();
+        String id = Integer.toHexString(System.identityHashCode(sub));
+        return sub.getClass().getSimpleName() + " [0x" + id + ", P" + priority + "]";
+    }
+
+    public Object getReference() {
+        return mSubscriber.get();
+    }
+}
+
+/**
+ * Represents an event handler with a priority.
+ */
+class EventHandler {
+    int priority;
+    Subscriber subscriber;
+    EventHandlerMethod method;
+
+    EventHandler(Subscriber subscriber, EventHandlerMethod method, int priority) {
+        this.subscriber = subscriber;
+        this.method = method;
+        this.priority = priority;
+    }
+
+    @Override
+    public String toString() {
+        return subscriber.toString(priority) + " " + method.toString();
+    }
+}
+
+/**
+ * Represents the low level method handling a particular event.
+ */
+class EventHandlerMethod {
+    private Method mMethod;
+    Class<? extends EventBus.Event> eventType;
+
+    EventHandlerMethod(Method method, Class<? extends EventBus.Event> eventType) {
+        mMethod = method;
+        mMethod.setAccessible(true);
+        this.eventType = eventType;
+    }
+
+    public void invoke(Object target, EventBus.Event event)
+            throws InvocationTargetException, IllegalAccessException {
+        mMethod.invoke(target, event);
+    }
+
+    @Override
+    public String toString() {
+        return mMethod.getName() + "(" + eventType.getSimpleName() + ")";
+    }
+}
+
+/**
+ * A simple in-process event bus.  It is simple because we can make assumptions about the state of
+ * SystemUI and Recent's lifecycle.
+ *
+ * <p>
+ * Currently, there is a single EventBus that handles {@link EventBus.Event}s for each subscriber
+ * on the main application thread.  Publishers can send() events to synchronously call subscribers
+ * of that event, or post() events to be processed in the next run of the {@link Looper}.  In
+ * addition, the EventBus supports sending and handling {@link EventBus.InterprocessEvent}s
+ * (within the same package) implemented using standard {@link BroadcastReceiver} mechanism.
+ * Interprocess events must be posted using postInterprocess() to ensure that it is dispatched
+ * correctly across processes.
+ *
+ * <p>
+ * Subscribers must be registered with a particular EventBus before they will receive events, and
+ * handler methods must match a specific signature.
+ *
+ * <p>
+ * Event method signature:<ul>
+ * <li>Methods must be public final
+ * <li>Methods must return void
+ * <li>Methods must be called "onBusEvent"
+ * <li>Methods must take one parameter, of class type deriving from {@link EventBus.Event}
+ * </ul>
+ *
+ * <p>
+ * Interprocess-Event method signature:<ul>
+ * <li>Methods must be public final
+ * <li>Methods must return void
+ * <li>Methods must be called "onInterprocessBusEvent"
+ * <li>Methods must take one parameter, of class type deriving from {@link EventBus.InterprocessEvent}
+ * </ul>
+ * </p>
+ *
+ * </p>
+ * Each subscriber can be registered with a given priority (default 1), and events will be dispatch
+ * in decreasing order of priority.  For subscribers with the same priority, events will be
+ * dispatched by latest registration time to earliest.
+ *
+ * <p>
+ * Interprocess events must extend {@link EventBus.InterprocessEvent}, have a constructor which
+ * takes a {@link Bundle} and implement toBundle().  This allows us to serialize events to be sent
+ * across processes.
+ *
+ * <p>
+ * Caveats:<ul>
+ * <li>The EventBus keeps a {@link WeakReference} to the publisher to prevent memory leaks, so
+ * there must be another strong reference to the publisher for it to not get garbage-collected and
+ * continue receiving events.
+ * <li>Because the event handlers are called back using reflection, the EventBus is not intended
+ * for use in tight, performance criticial loops.  For most user input/system callback events, this
+ * is generally of low enough frequency to use the EventBus.
+ * <li>Because the event handlers are called back using reflection, there will often be no
+ * references to them from actual code.  The proguard configuration will be need to be updated to
+ * keep these extra methods:
+ *
+ * -keepclassmembers class ** {
+ * public void onBusEvent(**);
+ * public void onInterprocessBusEvent(**);
+ * }
+ * -keepclassmembers class ** extends **.EventBus$InterprocessEvent {
+ * public <init>(android.os.Bundle);
+ * }
+ *
+ * <li>Subscriber registration can be expensive depending on the subscriber's {@link Class}.  This
+ * is only done once per class type, but if possible, it is best to pre-register an instance of
+ * that class beforehand or when idle.
+ * <li>Each event should be sent once.  Events may hold internal information about the current
+ * dispatch, or may be queued to be dispatched on another thread (if posted from a non-main thread),
+ * so it may be unsafe to edit, change, or re-send the event again.
+ * <li>Events should follow a pattern of public-final POD (plain old data) objects, where they are
+ * initialized by the constructor and read by each subscriber of that event.  Subscribers should
+ * never alter events as they are processed, and this enforces that pattern.
+ * </ul>
+ *
+ * <p>
+ * Future optimizations:
+ * <li>throw exception/log when a subscriber loses the reference
+ * <li>trace cost per registration & invocation
+ * <li>trace cross-process invocation
+ * <li>register(subscriber, Class&lt;?&gt;...) -- pass in exact class types you want registered
+ * <li>setSubscriberEventHandlerPriority(subscriber, Class<Event>, priority)
+ * <li>allow subscribers to implement interface, ie. EventBus.Subscriber, which lets then test a
+ * message before invocation (ie. check if task id == this task id)
+ * <li>add postOnce() which automatically debounces
+ * <li>add postDelayed() which delays / postDelayedOnce() which delays and bounces
+ * <li>consolidate register() and registerInterprocess()
+ * <li>sendForResult&lt;ReturnType&gt;(Event) to send and get a result, but who will send the
+ * result?
+ * </p>
+ */
+public class EventBus extends BroadcastReceiver {
+
+    public static final String TAG = "EventBus";
+
+    /**
+     * An event super class that allows us to track internal event state across subscriber
+     * invocations.
+     *
+     * Events should not be edited by subscribers.
+     */
+    public static class Event {
+        // Indicates that this event's dispatch should be traced and logged to logcat
+        boolean trace;
+        // Indicates that this event must be posted on the EventBus's looper thread before invocation
+        boolean requiresPost;
+        // Not currently exposed, allows a subscriber to cancel further dispatch of this event
+        boolean cancelled;
+
+        // Only accessible from derived events
+        protected Event() {}
+    }
+
+    /**
+     * An inter-process event super class that allows us to track user state across subscriber
+     * invocations.
+     */
+    public static class InterprocessEvent extends Event {
+        private static final String EXTRA_USER = "_user";
+
+        // The user which this event originated from
+        public final int user;
+
+        // Only accessible from derived events
+        protected InterprocessEvent(int user) {
+            this.user = user;
+        }
+
+        /**
+         * Called from the event bus
+         */
+        protected InterprocessEvent(Bundle b) {
+            user = b.getInt(EXTRA_USER);
+        }
+
+        protected Bundle toBundle() {
+            Bundle b = new Bundle();
+            b.putInt(EXTRA_USER, user);
+            return b;
+        }
+    }
+
+    /**
+     * Proguard must also know, and keep, all methods matching this signature.
+     *
+     * -keepclassmembers class ** {
+     *     public void onBusEvent(**);
+     *     public void onInterprocessBusEvent(**);
+     * }
+     */
+    private static final String METHOD_PREFIX = "onBusEvent";
+    private static final String INTERPROCESS_METHOD_PREFIX = "onInterprocessBusEvent";
+
+    // Ensures that interprocess events can only be sent from a process holding this permission. */
+    private static final String PERMISSION_SELF = "com.android.systemui.permission.SELF";
+
+    // Used for passing event data across process boundaries
+    private static final String EXTRA_INTERPROCESS_EVENT_BUNDLE = "interprocess_event_bundle";
+
+    // The default priority of all subscribers
+    private static final int DEFAULT_SUBSCRIBER_PRIORITY = 1;
+
+    // Used for debugging everything
+    private static final boolean DEBUG_TRACE_ALL = false;
+
+    // Orders the handlers by priority and registration time
+    private static final Comparator<EventHandler> EVENT_HANDLER_COMPARATOR = new Comparator<EventHandler>() {
+        @Override
+        public int compare(EventHandler h1, EventHandler h2) {
+            // Rank the handlers by priority descending, followed by registration time descending.
+            // aka. the later registered
+            if (h1.priority != h2.priority) {
+                return h2.priority - h1.priority;
+            } else {
+                return Long.compare(h2.subscriber.registrationTime, h1.subscriber.registrationTime);
+            }
+        }
+    };
+
+    // Used for initializing the default bus
+    private static final Object sLock = new Object();
+    private static EventBus sDefaultBus;
+
+    // The handler to post all events
+    private Handler mHandler;
+
+    // Keep track of whether we have registered a broadcast receiver already, so that we can
+    // unregister ourselves before re-registering again with a new IntentFilter.
+    private boolean mHasRegisteredReceiver;
+
+    /**
+     * Map from event class -> event handler list.  Keeps track of the actual mapping from event
+     * to subscriber method.
+     */
+    private HashMap<Class<? extends Event>, ArrayList<EventHandler>> mEventTypeMap = new HashMap<>();
+
+    /**
+     * Map from subscriber class -> event handler method lists.  Used to determine upon registration
+     * of a new subscriber whether we need to read all the subscriber's methods again using
+     * reflection or whether we can just add the subscriber to the event type map.
+     */
+    private HashMap<Class<? extends Object>, ArrayList<EventHandlerMethod>> mSubscriberTypeMap = new HashMap<>();
+
+    /**
+     * Map from interprocess event name -> interprocess event class.  Used for mapping the event
+     * name after receiving the broadcast, to the event type.  After which a new instance is created
+     * and posted in the local process.
+     */
+    private HashMap<String, Class<? extends InterprocessEvent>> mInterprocessEventNameMap = new HashMap<>();
+
+    /**
+     * Set of all currently registered subscribers
+     */
+    private ArrayList<Subscriber> mSubscribers = new ArrayList<>();
+
+    // For tracing
+    private int mCallCount;
+    private long mCallDurationMicros;
+
+    /**
+     * Private constructor to create an event bus for a given looper.
+     */
+    private EventBus(Looper looper) {
+        mHandler = new Handler(looper);
+    }
+
+    /**
+     * @return the default event bus for the application's main thread.
+     */
+    public static EventBus getDefault() {
+        if (sDefaultBus == null)
+        synchronized (sLock) {
+            if (sDefaultBus == null) {
+                if (DEBUG_TRACE_ALL) {
+                    logWithPid("New EventBus");
+                }
+                sDefaultBus = new EventBus(Looper.getMainLooper());
+            }
+        }
+        return sDefaultBus;
+    }
+
+    /**
+     * Registers a subscriber to receive events with the default priority.
+     *
+     * @param subscriber the subscriber to handle events.  If this is the first instance of the
+     *                   subscriber's class type that has been registered, the class's methods will
+     *                   be scanned for appropriate event handler methods.
+     */
+    public void register(Object subscriber) {
+        registerSubscriber(subscriber, DEFAULT_SUBSCRIBER_PRIORITY, null);
+    }
+
+    /**
+     * Registers a subscriber to receive events with the given priority.
+     *
+     * @param subscriber the subscriber to handle events.  If this is the first instance of the
+     *                   subscriber's class type that has been registered, the class's methods will
+     *                   be scanned for appropriate event handler methods.
+     * @param priority the priority that this subscriber will receive events relative to other
+     *                 subscribers
+     */
+    public void register(Object subscriber, int priority) {
+        registerSubscriber(subscriber, priority, null);
+    }
+
+    /**
+     * Explicitly registers a subscriber to receive interprocess events with the default priority.
+     *
+     * @param subscriber the subscriber to handle events.  If this is the first instance of the
+     *                   subscriber's class type that has been registered, the class's methods will
+     *                   be scanned for appropriate event handler methods.
+     */
+    public void registerInterprocessAsCurrentUser(Context context, Object subscriber) {
+        registerInterprocessAsCurrentUser(context, subscriber, DEFAULT_SUBSCRIBER_PRIORITY);
+    }
+
+    /**
+     * Registers a subscriber to receive interprocess events with the given priority.
+     *
+     * @param subscriber the subscriber to handle events.  If this is the first instance of the
+     *                   subscriber's class type that has been registered, the class's methods will
+     *                   be scanned for appropriate event handler methods.
+     * @param priority the priority that this subscriber will receive events relative to other
+     *                 subscribers
+     */
+    public void registerInterprocessAsCurrentUser(Context context, Object subscriber, int priority) {
+        if (DEBUG_TRACE_ALL) {
+            logWithPid("registerInterprocessAsCurrentUser(" + subscriber.getClass().getSimpleName() + ")");
+        }
+
+        // Register the subscriber normally, and update the broadcast receiver filter if this is
+        // a new subscriber type with interprocess events
+        MutableBoolean hasInterprocessEventsChanged = new MutableBoolean(false);
+        registerSubscriber(subscriber, priority, hasInterprocessEventsChanged);
+        if (DEBUG_TRACE_ALL) {
+            logWithPid("hasInterprocessEventsChanged: " + hasInterprocessEventsChanged.value);
+        }
+        if (hasInterprocessEventsChanged.value) {
+            registerReceiverForInterprocessEvents(context);
+        }
+    }
+
+    /**
+     * Remove all EventHandlers pointing to the specified subscriber.  This does not remove the
+     * mapping of subscriber type to event handler method, in case new instances of this subscriber
+     * are registered.
+     */
+    public void unregister(Object subscriber) {
+        if (DEBUG_TRACE_ALL) {
+            logWithPid("unregister()");
+        }
+
+        // Fail immediately if we are being called from the non-main thread
+        long callingThreadId = Thread.currentThread().getId();
+        if (callingThreadId != mHandler.getLooper().getThread().getId()) {
+            throw new RuntimeException("Can not unregister() a subscriber from a non-main thread.");
+        }
+
+        // Return early if this is not a registered subscriber
+        if (!findRegisteredSubscriber(subscriber, true /* removeFoundSubscriber */)) {
+            return;
+        }
+
+        Class<?> subscriberType = subscriber.getClass();
+        ArrayList<EventHandlerMethod> subscriberMethods = mSubscriberTypeMap.get(subscriberType);
+        if (subscriberMethods != null) {
+            // For each of the event handlers the subscriber handles, remove all references of that
+            // handler
+            for (EventHandlerMethod method : subscriberMethods) {
+                ArrayList<EventHandler> eventHandlers = mEventTypeMap.get(method.eventType);
+                for (int i = eventHandlers.size() - 1; i >= 0; i--) {
+                    if (eventHandlers.get(i).subscriber.getReference() == subscriber) {
+                        eventHandlers.remove(i);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Explicit unregistration for interprocess event subscribers.  This actually behaves exactly
+     * the same as unregister() since we also do not want to stop listening for specific
+     * inter-process messages in case new instances of that subscriber is registered.
+     */
+    public void unregisterInterprocess(Context context, Object subscriber) {
+        if (DEBUG_TRACE_ALL) {
+            logWithPid("unregisterInterprocess()");
+        }
+        unregister(subscriber);
+    }
+
+    /**
+     * Sends an event to the subscribers of the given event type immediately.  This can only be
+     * called from the same thread as the EventBus's looper thread (for the default EventBus, this
+     * is the main application thread).
+     */
+    public void send(Event event) {
+        // Fail immediately if we are being called from the non-main thread
+        long callingThreadId = Thread.currentThread().getId();
+        if (callingThreadId != mHandler.getLooper().getThread().getId()) {
+            throw new RuntimeException("Can not send() a message from a non-main thread.");
+        }
+
+        if (DEBUG_TRACE_ALL) {
+            logWithPid("send(" + event.getClass().getSimpleName() + ")");
+        }
+
+        // Reset the event's cancelled state
+        event.requiresPost = false;
+        event.cancelled = false;
+        queueEvent(event);
+    }
+
+    /**
+     * Post a message to the subscribers of the given event type.  The messages will be posted on
+     * the EventBus's looper thread (for the default EventBus, this is the main application thread).
+     */
+    public void post(Event event) {
+        if (DEBUG_TRACE_ALL) {
+            logWithPid("post(" + event.getClass().getSimpleName() + ")");
+        }
+
+        // Reset the event's cancelled state
+        event.requiresPost = true;
+        event.cancelled = false;
+        queueEvent(event);
+    }
+
+    /** Prevent post()ing an InterprocessEvent */
+    @Deprecated
+    public void post(InterprocessEvent event) {
+        throw new RuntimeException("Not supported, use postInterprocess");
+    }
+
+    /** Prevent send()ing an InterprocessEvent */
+    @Deprecated
+    public void send(InterprocessEvent event) {
+        throw new RuntimeException("Not supported, use postInterprocess");
+    }
+
+    /**
+     * Posts an interprocess event.
+     */
+    public void postInterprocess(Context context, final InterprocessEvent event) {
+        if (DEBUG_TRACE_ALL) {
+            logWithPid("postInterprocess(" + event.getClass().getSimpleName() + ")");
+        }
+        String eventType = event.getClass().getName();
+        Bundle eventBundle = event.toBundle();
+        Intent intent = new Intent(eventType);
+        intent.setPackage(context.getPackageName());
+        intent.putExtra(EXTRA_INTERPROCESS_EVENT_BUNDLE, eventBundle);
+        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT |
+                Intent.FLAG_RECEIVER_FOREGROUND);
+        context.sendBroadcastAsUser(intent, UserHandle.ALL);
+    }
+
+    /**
+     * Receiver for interprocess events.
+     */
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        if (DEBUG_TRACE_ALL) {
+            logWithPid("onReceive(" + intent.getAction() + ", user " + UserHandle.myUserId() + ")");
+        }
+
+        Bundle eventBundle = intent.getBundleExtra(EXTRA_INTERPROCESS_EVENT_BUNDLE);
+        Class<? extends InterprocessEvent> eventType = mInterprocessEventNameMap.get(intent.getAction());
+        try {
+            Constructor<? extends InterprocessEvent> ctor = eventType.getConstructor(Bundle.class);
+            send((Event) ctor.newInstance(eventBundle));
+        } catch (NoSuchMethodException|
+                InvocationTargetException|
+                InstantiationException|
+                IllegalAccessException e) {
+            Log.e(TAG, "Failed to create InterprocessEvent", e);
+        }
+    }
+
+    /**
+     * @return a dump of the current state of the EventBus
+     */
+    public String dump() {
+        StringBuilder output = new StringBuilder();
+        output.append("Registered class types:");
+        output.append("\n");
+        for (Class<?> clz : mSubscriberTypeMap.keySet()) {
+            output.append("\t");
+            output.append(clz.getSimpleName());
+            output.append("\n");
+        }
+        output.append("Event map:");
+        output.append("\n");
+        for (Class<?> clz : mEventTypeMap.keySet()) {
+            output.append("\t");
+            output.append(clz.getSimpleName());
+            output.append(" -> ");
+            output.append("\n");
+            ArrayList<EventHandler> handlers = mEventTypeMap.get(clz);
+            for (EventHandler handler : handlers) {
+                Object subscriber = handler.subscriber.getReference();
+                if (subscriber != null) {
+                    String id = Integer.toHexString(System.identityHashCode(subscriber));
+                    output.append("\t\t");
+                    output.append(subscriber.getClass().getSimpleName());
+                    output.append(" [0x" + id + ", #" + handler.priority + "]");
+                    output.append("\n");
+                }
+            }
+        }
+        return output.toString();
+    }
+
+    /**
+     * Registers a new subscriber.
+     *
+     * @return return whether or not this
+     */
+    private void registerSubscriber(Object subscriber, int priority,
+            MutableBoolean hasInterprocessEventsChangedOut) {
+        // Fail immediately if we are being called from the non-main thread
+        long callingThreadId = Thread.currentThread().getId();
+        if (callingThreadId != mHandler.getLooper().getThread().getId()) {
+            throw new RuntimeException("Can not register() a subscriber from a non-main thread.");
+        }
+
+        // Return immediately if this exact subscriber is already registered
+        if (findRegisteredSubscriber(subscriber, false /* removeFoundSubscriber */)) {
+            return;
+        }
+
+        long t1 = 0;
+        if (DEBUG_TRACE_ALL) {
+            t1 = SystemClock.currentTimeMicro();
+            logWithPid("registerSubscriber(" + subscriber.getClass().getSimpleName() + ")");
+        }
+        Subscriber sub = new Subscriber(subscriber, SystemClock.uptimeMillis());
+        Class<?> subscriberType = subscriber.getClass();
+        ArrayList<EventHandlerMethod> subscriberMethods = mSubscriberTypeMap.get(subscriberType);
+        if (subscriberMethods != null) {
+            if (DEBUG_TRACE_ALL) {
+                logWithPid("Subscriber class type already registered");
+            }
+
+            // If we've parsed this subscriber type before, just add to the set for all the known
+            // events
+            for (EventHandlerMethod method : subscriberMethods) {
+                ArrayList<EventHandler> eventTypeHandlers = mEventTypeMap.get(method.eventType);
+                eventTypeHandlers.add(new EventHandler(sub, method, priority));
+                sortEventHandlersByPriority(eventTypeHandlers);
+            }
+            mSubscribers.add(sub);
+            return;
+        } else {
+            if (DEBUG_TRACE_ALL) {
+                logWithPid("Subscriber class type requires registration");
+            }
+
+            // If we are parsing this type from scratch, ensure we add it to the subscriber type
+            // map, and pull out he handler methods below
+            subscriberMethods = new ArrayList<>();
+            mSubscriberTypeMap.put(subscriberType, subscriberMethods);
+            mSubscribers.add(sub);
+        }
+
+        // Find all the valid event bus handler methods of the subscriber
+        MutableBoolean isInterprocessEvent = new MutableBoolean(false);
+        Method[] methods = subscriberType.getDeclaredMethods();
+        for (Method m : methods) {
+            Class<?>[] parameterTypes = m.getParameterTypes();
+            isInterprocessEvent.value = false;
+            if (isValidEventBusHandlerMethod(m, parameterTypes, isInterprocessEvent)) {
+                Class<? extends Event> eventType = (Class<? extends Event>) parameterTypes[0];
+                ArrayList<EventHandler> eventTypeHandlers = mEventTypeMap.get(eventType);
+                if (eventTypeHandlers == null) {
+                    eventTypeHandlers = new ArrayList<>();
+                    mEventTypeMap.put(eventType, eventTypeHandlers);
+                }
+                if (isInterprocessEvent.value) {
+                    try {
+                        // Enforce that the event must have a Bundle constructor
+                        eventType.getConstructor(Bundle.class);
+
+                        mInterprocessEventNameMap.put(eventType.getName(),
+                                (Class<? extends InterprocessEvent>) eventType);
+                        if (hasInterprocessEventsChangedOut != null) {
+                            hasInterprocessEventsChangedOut.value = true;
+                        }
+                    } catch (NoSuchMethodException e) {
+                        throw new RuntimeException("Expected InterprocessEvent to have a Bundle constructor");
+                    }
+                }
+                EventHandlerMethod method = new EventHandlerMethod(m, eventType);
+                EventHandler handler = new EventHandler(sub, method, priority);
+                eventTypeHandlers.add(handler);
+                subscriberMethods.add(method);
+                sortEventHandlersByPriority(eventTypeHandlers);
+
+                if (DEBUG_TRACE_ALL) {
+                    logWithPid("  * Method: " + m.getName() +
+                            " event: " + parameterTypes[0].getSimpleName() +
+                            " interprocess? " + isInterprocessEvent.value);
+                }
+            }
+        }
+        if (DEBUG_TRACE_ALL) {
+            logWithPid("Registered " + subscriber.getClass().getSimpleName() + " in " +
+                    (SystemClock.currentTimeMicro() - t1) + " microseconds");
+        }
+    }
+
+    /**
+     * Adds a new message.
+     */
+    private void queueEvent(final Event event) {
+        ArrayList<EventHandler> eventHandlers = mEventTypeMap.get(event.getClass());
+        if (eventHandlers == null) {
+            return;
+        }
+        // We need to clone the list in case a subscriber unregisters itself during traversal
+        eventHandlers = (ArrayList<EventHandler>) eventHandlers.clone();
+        for (final EventHandler eventHandler : eventHandlers) {
+            if (eventHandler.subscriber.getReference() != null) {
+                if (event.requiresPost) {
+                    mHandler.post(new Runnable() {
+                        @Override
+                        public void run() {
+                            processEvent(eventHandler, event);
+                        }
+                    });
+                } else {
+                    processEvent(eventHandler, event);
+                }
+            }
+        }
+    }
+
+    /**
+     * Processes and dispatches the given event to the given event handler, on the thread of whoever
+     * calls this method.
+     */
+    private void processEvent(final EventHandler eventHandler, final Event event) {
+        // Skip if the event was already cancelled
+        if (event.cancelled) {
+            if (event.trace || DEBUG_TRACE_ALL) {
+                logWithPid("Event dispatch cancelled");
+            }
+            return;
+        }
+
+        try {
+            if (event.trace || DEBUG_TRACE_ALL) {
+                logWithPid(" -> " + eventHandler.toString());
+            }
+            Object sub = eventHandler.subscriber.getReference();
+            if (sub != null) {
+                long t1 = 0;
+                if (DEBUG_TRACE_ALL) {
+                    t1 = SystemClock.currentTimeMicro();
+                }
+                eventHandler.method.invoke(sub, event);
+                if (DEBUG_TRACE_ALL) {
+                    long duration = (SystemClock.currentTimeMicro() - t1);
+                    mCallDurationMicros += duration;
+                    mCallCount++;
+                    logWithPid(eventHandler.method.toString() + " duration: " + duration +
+                            " microseconds, avg: " + (mCallDurationMicros / mCallCount));
+                }
+            } else {
+                Log.e(TAG, "Failed to deliver event to null subscriber");
+            }
+        } catch (IllegalAccessException e) {
+            Log.e(TAG, "Failed to invoke method", e);
+        } catch (InvocationTargetException e) {
+            throw new RuntimeException(e.getCause());
+        }
+    }
+
+    /**
+     * Re-registers the broadcast receiver for any new messages that we want to listen for.
+     */
+    private void registerReceiverForInterprocessEvents(Context context) {
+        if (DEBUG_TRACE_ALL) {
+            logWithPid("registerReceiverForInterprocessEvents()");
+        }
+        // Rebuild the receiver filter with the new interprocess events
+        IntentFilter filter = new IntentFilter();
+        for (String eventName : mInterprocessEventNameMap.keySet()) {
+            filter.addAction(eventName);
+            if (DEBUG_TRACE_ALL) {
+                logWithPid("  filter: " + eventName);
+            }
+        }
+        // Re-register the receiver with the new filter
+        if (mHasRegisteredReceiver) {
+            context.unregisterReceiver(this);
+        }
+        context.registerReceiverAsUser(this, UserHandle.ALL, filter, PERMISSION_SELF, mHandler);
+        mHasRegisteredReceiver = true;
+    }
+
+    /**
+     * Returns whether this subscriber is currently registered.  If {@param removeFoundSubscriber}
+     * is true, then remove the subscriber before returning.
+     */
+    private boolean findRegisteredSubscriber(Object subscriber, boolean removeFoundSubscriber) {
+        for (int i = mSubscribers.size() - 1; i >= 0; i--) {
+            Subscriber sub = mSubscribers.get(i);
+            if (sub.getReference() == subscriber) {
+                if (removeFoundSubscriber) {
+                    mSubscribers.remove(i);
+                }
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * @return whether {@param method} is a valid (normal or interprocess) event bus handler method
+     */
+    private boolean isValidEventBusHandlerMethod(Method method, Class<?>[] parameterTypes,
+            MutableBoolean isInterprocessEventOut) {
+        int modifiers = method.getModifiers();
+        if (Modifier.isPublic(modifiers) &&
+                Modifier.isFinal(modifiers) &&
+                method.getReturnType().equals(Void.TYPE) &&
+                parameterTypes.length == 1) {
+            if (EventBus.InterprocessEvent.class.isAssignableFrom(parameterTypes[0]) &&
+                    method.getName().startsWith(INTERPROCESS_METHOD_PREFIX)) {
+                isInterprocessEventOut.value = true;
+                return true;
+            } else if (EventBus.Event.class.isAssignableFrom(parameterTypes[0]) &&
+                            method.getName().startsWith(METHOD_PREFIX)) {
+                isInterprocessEventOut.value = false;
+                return true;
+            } else {
+                if (DEBUG_TRACE_ALL) {
+                    if (!EventBus.Event.class.isAssignableFrom(parameterTypes[0])) {
+                        logWithPid("  Expected method take an Event-based parameter: " + method.getName());
+                    } else if (!method.getName().startsWith(INTERPROCESS_METHOD_PREFIX) &&
+                            !method.getName().startsWith(METHOD_PREFIX)) {
+                        logWithPid("  Expected method start with method prefix: " + method.getName());
+                    }
+                }
+            }
+        } else {
+            if (DEBUG_TRACE_ALL) {
+                if (!Modifier.isPublic(modifiers)) {
+                    logWithPid("  Expected method to be public: " + method.getName());
+                } else if (!Modifier.isFinal(modifiers)) {
+                    logWithPid("  Expected method to be final: " + method.getName());
+                } else if (!method.getReturnType().equals(Void.TYPE)) {
+                    logWithPid("  Expected method to return null: " + method.getName());
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Sorts the event handlers by priority and registration time.
+     */
+    private void sortEventHandlersByPriority(List<EventHandler> eventHandlers) {
+        Collections.sort(eventHandlers, EVENT_HANDLER_COMPARATOR);
+    }
+
+    /**
+     * Helper method to log the given {@param text} with the current process and user id.
+     */
+    private static void logWithPid(String text) {
+        Log.d(TAG, "[" + android.os.Process.myPid() + ", u" + UserHandle.myUserId() + "] " + text);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/AppWidgetProviderChangedEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/AppWidgetProviderChangedEvent.java
new file mode 100644
index 0000000..52cfe18
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/AppWidgetProviderChangedEvent.java
@@ -0,0 +1,28 @@
+/*
+ * 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.recents.events.activity;
+
+import com.android.systemui.recents.RecentsAppWidgetHost;
+import com.android.systemui.recents.events.EventBus;
+
+/**
+ * This is sent by the {@link RecentsAppWidgetHost} whenever the search provider widget changes, and
+ * subscribers can update accordingly.
+ */
+public class AppWidgetProviderChangedEvent extends EventBus.Event {
+    // Simple event
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/PackagesChangedEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/PackagesChangedEvent.java
new file mode 100644
index 0000000..3b68574
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/PackagesChangedEvent.java
@@ -0,0 +1,39 @@
+/*
+ * 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.recents.events.activity;
+
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.model.RecentsPackageMonitor;
+import com.android.systemui.recents.views.TaskStackView;
+
+/**
+ * This event is sent by {@link RecentsPackageMonitor} when a package on the the system changes.
+ * {@link TaskStackView}s listen for this event, and remove the tasks associated with the removed
+ * packages.
+ */
+public class PackagesChangedEvent extends EventBus.Event {
+
+    public final RecentsPackageMonitor monitor;
+    public final String packageName;
+    public final int userId;
+
+    public PackagesChangedEvent(RecentsPackageMonitor monitor, String packageName, int userId) {
+        this.monitor = monitor;
+        this.packageName = packageName;
+        this.userId = userId;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/DismissTaskEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/DismissTaskEvent.java
new file mode 100644
index 0000000..12e5d3d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/DismissTaskEvent.java
@@ -0,0 +1,35 @@
+/*
+ * 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.recents.events.ui;
+
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.model.Task;
+import com.android.systemui.recents.views.TaskView;
+
+/**
+ * This is sent when a {@link TaskView} has been dismissed.
+ */
+public class DismissTaskEvent extends EventBus.Event {
+
+    public final Task task;
+    public final TaskView taskView;
+
+    public DismissTaskEvent(Task task, TaskView taskView) {
+        this.task = task;
+        this.taskView = taskView;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/ResizeTaskEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/ResizeTaskEvent.java
new file mode 100644
index 0000000..e0d83fd
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/ResizeTaskEvent.java
@@ -0,0 +1,32 @@
+/*
+ * 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.recents.events.ui;
+
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.model.Task;
+
+/**
+ * This is sent when a {@link Task} is resized.
+ */
+public class ResizeTaskEvent extends EventBus.Event {
+
+    public final Task task;
+
+    public ResizeTaskEvent(Task task) {
+        this.task = task;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/ShowApplicationInfoEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/ShowApplicationInfoEvent.java
new file mode 100644
index 0000000..40c30b8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/ShowApplicationInfoEvent.java
@@ -0,0 +1,32 @@
+/*
+ * 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.recents.events.ui;
+
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.model.Task;
+
+/**
+ * This is sent when a user wants to show the application info for a {@link Task}.
+ */
+public class ShowApplicationInfoEvent extends EventBus.Event {
+
+    public final Task task;
+
+    public ShowApplicationInfoEvent(Task task) {
+        this.task = task;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragDockStateChangedEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragDockStateChangedEvent.java
new file mode 100644
index 0000000..f2c3c33
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragDockStateChangedEvent.java
@@ -0,0 +1,35 @@
+/*
+ * 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.recents.events.ui.dragndrop;
+
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.model.Task;
+import com.android.systemui.recents.model.TaskStack;
+
+/**
+ * This event is sent when a user drag enters or exits a dock region.
+ */
+public class DragDockStateChangedEvent extends EventBus.Event {
+
+    public final Task task;
+    public final TaskStack.DockState dockState;
+
+    public DragDockStateChangedEvent(Task task, TaskStack.DockState dockState) {
+        this.task = task;
+        this.dockState = dockState;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragEndEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragEndEvent.java
new file mode 100644
index 0000000..827998d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragEndEvent.java
@@ -0,0 +1,45 @@
+/*
+ * 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.recents.events.ui.dragndrop;
+
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.misc.ReferenceCountedTrigger;
+import com.android.systemui.recents.model.Task;
+import com.android.systemui.recents.model.TaskStack;
+import com.android.systemui.recents.views.DragView;
+import com.android.systemui.recents.views.TaskView;
+
+/**
+ * This event is sent whenever a drag ends.
+ */
+public class DragEndEvent extends EventBus.Event {
+
+    public final Task task;
+    public final TaskView taskView;
+    public final DragView dragView;
+    public final TaskStack.DockState dockState;
+    public final ReferenceCountedTrigger postAnimationTrigger;
+
+    public DragEndEvent(Task task, TaskView taskView, DragView dragView,
+            TaskStack.DockState dockState, ReferenceCountedTrigger postAnimationTrigger) {
+        this.task = task;
+        this.taskView = taskView;
+        this.dragView = dragView;
+        this.dockState = dockState;
+        this.postAnimationTrigger = postAnimationTrigger;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragStartEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragStartEvent.java
new file mode 100644
index 0000000..2d42a0e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragStartEvent.java
@@ -0,0 +1,38 @@
+/*
+ * 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.recents.events.ui.dragndrop;
+
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.model.Task;
+import com.android.systemui.recents.views.DragView;
+import com.android.systemui.recents.views.TaskView;
+
+/**
+ * This event is sent whenever a drag starts.
+ */
+public class DragStartEvent extends EventBus.Event {
+
+    public final Task task;
+    public final TaskView taskView;
+    public final DragView dragView;
+
+    public DragStartEvent(Task task, TaskView taskView, DragView dragView) {
+        this.task = task;
+        this.taskView = taskView;
+        this.dragView = dragView;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index b6d25f5..568d2b1 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -16,6 +16,9 @@
 
 package com.android.systemui.recents.misc;
 
+import static android.app.ActivityManager.DOCKED_STACK_ID;
+import static android.app.ActivityManager.INVALID_STACK_ID;
+
 import android.app.ActivityManager;
 import android.app.ActivityManagerNative;
 import android.app.ActivityOptions;
@@ -55,10 +58,7 @@
 import android.provider.Settings;
 import android.util.Log;
 import android.util.MutableBoolean;
-import android.util.MutableFloat;
-import android.util.MutableInt;
 import android.util.Pair;
-import android.util.Size;
 import android.view.Display;
 import android.view.WindowManager;
 import android.view.accessibility.AccessibilityManager;
@@ -300,7 +300,7 @@
         if (mIam == null) return;
 
         try {
-            mIam.moveTaskToDockedStack(taskId, createMode, true);
+            mIam.startActivityFromRecents(taskId, DOCKED_STACK_ID, null);
         } catch (RemoteException e) {
             e.printStackTrace();
         }
@@ -330,6 +330,21 @@
         return mAm.isInHomeStack(taskId);
     }
 
+    /**
+     * @return whether there are any docked tasks.
+     */
+    public boolean hasDockedTask() {
+        if (mIam == null) return false;
+
+        ActivityManager.StackInfo stackInfo = null;
+        try {
+            stackInfo = mIam.getStackInfo(DOCKED_STACK_ID);
+        } catch (RemoteException e) {
+            e.printStackTrace();
+        }
+        return stackInfo != null;
+    }
+
     /** Returns the top task thumbnail for the given task id */
     public Bitmap getTaskThumbnail(int taskId) {
         if (mAm == null) return null;
@@ -710,7 +725,8 @@
             ActivityOptions options) {
         if (mIam != null) {
             try {
-                mIam.startActivityFromRecents(taskId, options == null ? null : options.toBundle());
+                mIam.startActivityFromRecents(
+                        taskId, INVALID_STACK_ID, options == null ? null : options.toBundle());
                 return true;
             } catch (Exception e) {
                 Console.logError(context,
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java b/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
index e810410..93c5ee7 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
@@ -21,12 +21,29 @@
 import android.graphics.Matrix;
 import android.graphics.Rect;
 import android.view.View;
+import android.view.ViewParent;
 
 import java.util.ArrayList;
 
 /* Common code */
 public class Utilities {
 
+    /**
+     * @return the first parent walking up the view hierarchy that has the given class type.
+     *
+     * @param parentClass must be a class derived from {@link View}
+     */
+    public static <T extends View> T findParent(View v, Class<T> parentClass) {
+        ViewParent parent = v.getParent();
+        while (parent != null) {
+            if (parent.getClass().equals(parentClass)) {
+                return (T) parent;
+            }
+            parent = parent.getParent();
+        }
+        return null;
+    }
+
     /** Scales a rect about its centroid */
     public static void scaleRectAboutCenter(Rect r, float scale) {
         if (scale != 1.0f) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsPackageMonitor.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsPackageMonitor.java
index e48e5f0..8f9a293 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsPackageMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsPackageMonitor.java
@@ -21,6 +21,8 @@
 import android.os.Looper;
 import android.os.UserHandle;
 import com.android.internal.content.PackageMonitor;
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.events.activity.PackagesChangedEvent;
 import com.android.systemui.recents.misc.SystemServicesProxy;
 
 import java.util.HashSet;
@@ -31,18 +33,9 @@
  * Recents list.
  */
 public class RecentsPackageMonitor extends PackageMonitor {
-    public interface PackageCallbacks {
-        public void onPackagesChanged(RecentsPackageMonitor monitor, String packageName,
-                                      int userId);
-    }
-
-    PackageCallbacks mCb;
-    SystemServicesProxy mSystemServicesProxy;
 
     /** Registers the broadcast receivers with the specified callbacks. */
-    public void register(Context context, PackageCallbacks cb) {
-        mSystemServicesProxy = new SystemServicesProxy(context);
-        mCb = cb;
+    public void register(Context context) {
         try {
             // We register for events from all users, but will cross-reference them with
             // packages for the current user and any profiles they have
@@ -60,17 +53,13 @@
         } catch (IllegalStateException e) {
             e.printStackTrace();
         }
-        mSystemServicesProxy = null;
-        mCb = null;
     }
 
     @Override
     public void onPackageRemoved(String packageName, int uid) {
-        if (mCb == null) return;
-
         // Notify callbacks that a package has changed
         final int eventUserId = getChangingUserId();
-        mCb.onPackagesChanged(this, packageName, eventUserId);
+        EventBus.getDefault().send(new PackagesChangedEvent(this, packageName, eventUserId));
     }
 
     @Override
@@ -81,11 +70,9 @@
 
     @Override
     public void onPackageModified(String packageName) {
-        if (mCb == null) return;
-
         // Notify callbacks that a package has changed
         final int eventUserId = getChangingUserId();
-        mCb.onPackagesChanged(this, packageName, eventUserId);
+        EventBus.getDefault().send(new PackagesChangedEvent(this, packageName, eventUserId));
     }
 
     /**
@@ -108,7 +95,8 @@
                     // If we know that the component still exists in the package, then skip
                     continue;
                 }
-                if (mSystemServicesProxy.getActivityInfo(cn, userId) != null) {
+                SystemServicesProxy ssp = RecentsTaskLoader.getInstance().getSystemServicesProxy();
+                if (ssp.getActivityInfo(cn, userId) != null) {
                     existingComponents.add(cn);
                 } else {
                     removedComponents.add(cn);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
index 760382e..39bef81 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
@@ -262,8 +262,6 @@
     TaskResourceLoadQueue mLoadQueue;
     TaskResourceLoader mLoader;
 
-    RecentsPackageMonitor mPackageMonitor;
-
     int mMaxThumbnailCacheSize;
     int mMaxIconCacheSize;
     int mNumVisibleTasksLoaded;
@@ -293,7 +291,6 @@
 
         // Initialize the proxy, cache and loaders
         mSystemServicesProxy = new SystemServicesProxy(context);
-        mPackageMonitor = new RecentsPackageMonitor();
         mLoadQueue = new TaskResourceLoadQueue();
         mApplicationIconCache = new DrawableLruCache(iconCacheSize);
         mThumbnailCache = new BitmapLruCache(thumbnailCacheSize);
@@ -519,17 +516,6 @@
         mLoadQueue.clearTasks();
     }
 
-    /** Registers any broadcast receivers. */
-    public void registerReceivers(Context context, RecentsPackageMonitor.PackageCallbacks cb) {
-        // Register the broadcast receiver to handle messages related to packages being added/removed
-        mPackageMonitor.register(context, cb);
-    }
-
-    /** Unregisters any broadcast receivers. */
-    public void unregisterReceivers() {
-        mPackageMonitor.unregister();
-    }
-
     /**
      * Handles signals from the system, trimming memory when requested to prevent us from running
      * out of memory.
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
index 20d9203..0fb235b 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
@@ -16,12 +16,15 @@
 
 package com.android.systemui.recents.model;
 
+import android.app.ActivityManager;
 import android.content.Context;
 import android.graphics.Color;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import com.android.systemui.R;
 import com.android.systemui.recents.Constants;
 import com.android.systemui.recents.misc.NamedCounter;
 import com.android.systemui.recents.misc.Utilities;
-import com.android.systemui.R;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -30,6 +33,9 @@
 import java.util.List;
 import java.util.Random;
 
+import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT;
+import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
+
 
 /**
  * An interface for a task filter to query whether a particular task should show in a stack.
@@ -174,6 +180,63 @@
         public void onStackUnfiltered(TaskStack newStack, ArrayList<Task> curTasks);
     }
 
+
+    public enum DockState {
+        LEFT(DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT,
+                new RectF(0, 0, 0.25f, 1), new RectF(0, 0, 0.5f, 1), new RectF(0.5f, 0, 1, 1)),
+        TOP(DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT,
+                new RectF(0, 0, 1, 0.25f), new RectF(0, 0, 1, 0.5f), new RectF(0, 0.5f, 1, 1)),
+        RIGHT(DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT,
+                new RectF(0.75f, 0, 1, 1), new RectF(0.5f, 0, 1, 1), new RectF(0, 0, 0.5f, 1)),
+        BOTTOM(DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT,
+                new RectF(0, 0.75f, 1, 1), new RectF(0, 0.5f, 1, 1), new RectF(0, 0, 1, 0.5f));
+
+        public final int createMode;
+        private final RectF touchArea;
+        private final RectF dockArea;
+        private final RectF stackArea;
+
+        /**
+         * @param createMode used to pass to ActivityManager to dock the task
+         * @param touchArea the area in which touch will initiate this dock state
+         * @param stackArea the area for the stack if a task is docked
+         */
+        DockState(int createMode, RectF touchArea, RectF dockArea, RectF stackArea) {
+            this.createMode = createMode;
+            this.touchArea = touchArea;
+            this.dockArea = dockArea;
+            this.stackArea = stackArea;
+        }
+
+        /**
+         * Returns whether {@param x} and {@param y} are contained in the touch area scaled to the
+         * given {@param width} and {@param height}.
+         */
+        public boolean touchAreaContainsPoint(int width, int height, float x, float y) {
+            int left = (int) (touchArea.left * width);
+            int top = (int) (touchArea.top * height);
+            int right = (int) (touchArea.right * width);
+            int bottom = (int) (touchArea.bottom * height);
+            return x >= left && y >= top && x <= right && y <= bottom;
+        }
+
+        /**
+         * Returns the docked task bounds with the given {@param width} and {@param height}.
+         */
+        public Rect getDockedBounds(int width, int height) {
+            return new Rect((int) (dockArea.left * width), (int) (dockArea.top * height),
+                    (int) (dockArea.right * width), (int) (dockArea.bottom * height));
+        }
+
+        /**
+         * Returns the stack bounds with the given {@param width} and {@param height}.
+         */
+        public Rect getStackBounds(int width, int height) {
+            return new Rect((int) (stackArea.left * width), (int) (stackArea.top * height),
+                    (int) (stackArea.right * width), (int) (stackArea.bottom * height));
+        }
+    }
+
     // The task offset to apply to a task id as a group affiliation
     static final int IndividualTaskIdOffset = 1 << 16;
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/DragView.java b/packages/SystemUI/src/com/android/systemui/recents/views/DragView.java
new file mode 100644
index 0000000..96dfaac
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/DragView.java
@@ -0,0 +1,39 @@
+/*
+ * 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.recents.views;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Point;
+import android.widget.ImageView;
+
+public class DragView extends ImageView {
+
+    // The offset from the top-left of the dragBitmap
+    Point mTopLeftOffset;
+
+    public DragView(Context context, Bitmap dragBitmap, Point tlOffset) {
+        super(context);
+
+        mTopLeftOffset = tlOffset;
+        setImageBitmap(dragBitmap);
+    }
+
+    public Point getTopLeftOffset() {
+        return mTopLeftOffset;
+    }
+}
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 73c9be9..e406155 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -18,36 +18,39 @@
 
 import android.app.ActivityManager;
 import android.app.ActivityOptions;
-import android.app.TaskStackBuilder;
 import android.content.Context;
-import android.content.Intent;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Rect;
-import android.net.Uri;
+import android.graphics.drawable.ColorDrawable;
 import android.os.Bundle;
 import android.os.IRemoteCallback;
 import android.os.RemoteException;
-import android.os.UserHandle;
-import android.provider.Settings;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.SparseArray;
 import android.view.AppTransitionAnimationSpec;
 import android.view.LayoutInflater;
+import android.view.MotionEvent;
 import android.view.View;
 import android.view.WindowInsets;
 import android.view.WindowManagerGlobal;
+import android.view.animation.AccelerateInterpolator;
 import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
 import android.widget.FrameLayout;
 import com.android.internal.logging.MetricsLogger;
 import com.android.systemui.R;
 import com.android.systemui.recents.Constants;
+import com.android.systemui.recents.RecentsActivity;
 import com.android.systemui.recents.RecentsAppWidgetHostView;
 import com.android.systemui.recents.RecentsConfiguration;
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.events.ui.DismissTaskEvent;
+import com.android.systemui.recents.events.ui.dragndrop.DragDockStateChangedEvent;
+import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent;
+import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent;
 import com.android.systemui.recents.misc.SystemServicesProxy;
-import com.android.systemui.recents.model.RecentsPackageMonitor;
 import com.android.systemui.recents.model.RecentsTaskLoader;
 import com.android.systemui.recents.model.Task;
 import com.android.systemui.recents.model.TaskStack;
@@ -59,8 +62,7 @@
  * This view is the the top level layout that contains TaskStacks (which are laid out according
  * to their SpaceNode bounds.
  */
-public class RecentsView extends FrameLayout implements TaskStackView.TaskStackViewCallbacks,
-        RecentsPackageMonitor.PackageCallbacks {
+public class RecentsView extends FrameLayout implements TaskStackView.TaskStackViewCallbacks {
 
     private static final String TAG = "RecentsView";
 
@@ -73,7 +75,6 @@
         public void onAllTaskViewsDismissed();
         public void onExitToHomeAnimationTriggered();
         public void onScreenPinningRequest();
-        public void onTaskResize(Task t);
         public void runAfterPause(Runnable r);
     }
 
@@ -84,6 +85,11 @@
     TaskStackView mTaskStackView;
     RecentsAppWidgetHostView mSearchBar;
     RecentsViewCallbacks mCb;
+
+    RecentsViewTouchHandler mTouchHandler;
+    DragView mDragView;
+    ColorDrawable mDockRegionOverlay;
+
     Interpolator mFastOutSlowInInterpolator;
 
     Rect mSystemInsets = new Rect();
@@ -102,10 +108,14 @@
 
     public RecentsView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
+        setWillNotDraw(false);
         mConfig = RecentsConfiguration.getInstance();
         mInflater = LayoutInflater.from(context);
         mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
                 com.android.internal.R.interpolator.fast_out_slow_in);
+        mTouchHandler = new RecentsViewTouchHandler(this);
+        mDockRegionOverlay = new ColorDrawable(0x66000000);
+        mDockRegionOverlay.setAlpha(0);
     }
 
     /** Sets the callbacks */
@@ -206,6 +216,7 @@
             ArrayList<Task> tasks = stack.getTasks();
 
             // Find the launch task in the stack
+            // TODO: replace this with an event from RecentsActivity
             if (!tasks.isEmpty()) {
                 int taskCount = tasks.size();
                 for (int j = 0; j < taskCount; j++) {
@@ -273,6 +284,20 @@
         }
     }
 
+    @Override
+    protected void onAttachedToWindow() {
+        EventBus.getDefault().register(this, RecentsActivity.EVENT_BUS_PRIORITY + 1);
+        EventBus.getDefault().register(mTouchHandler, RecentsActivity.EVENT_BUS_PRIORITY + 1);
+        super.onAttachedToWindow();
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        EventBus.getDefault().unregister(this);
+        EventBus.getDefault().unregister(mTouchHandler);
+    }
+
     /**
      * This is called with the full size of the window since we are handling our own insets.
      */
@@ -299,6 +324,12 @@
             mTaskStackView.measure(widthMeasureSpec, heightMeasureSpec);
         }
 
+        if (mDragView != null) {
+            mDragView.measure(
+                    MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST),
+                    MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST));
+        }
+
         setMeasuredDimension(width, height);
     }
 
@@ -320,6 +351,11 @@
         if (mTaskStackView != null && mTaskStackView.getVisibility() != GONE) {
             mTaskStackView.layout(left, top, left + getMeasuredWidth(), top + getMeasuredHeight());
         }
+
+        if (mDragView != null) {
+            mDragView.layout(left, top, left + mDragView.getMeasuredWidth(),
+                    top + mDragView.getMeasuredHeight());
+        }
     }
 
     @Override
@@ -329,6 +365,24 @@
         return insets.consumeSystemWindowInsets();
     }
 
+    @Override
+    public boolean onInterceptTouchEvent(MotionEvent ev) {
+        return mTouchHandler.onInterceptTouchEvent(ev);
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent ev) {
+        return mTouchHandler.onTouchEvent(ev);
+    }
+
+    @Override
+    protected void dispatchDraw(Canvas canvas) {
+        super.dispatchDraw(canvas);
+        if (mDockRegionOverlay.getAlpha() > 0) {
+            mDockRegionOverlay.draw(canvas);
+        }
+    }
+
     /** Notifies each task view of the user interaction. */
     public void onUserInteraction() {
         // Get the first stack view
@@ -611,7 +665,7 @@
                     } else {
                         // Dismiss the task and return the user to home if we fail to
                         // launch the task
-                        onTaskViewDismissed(task);
+                        EventBus.getDefault().send(new DismissTaskEvent(task, tv));
                         if (mCb != null) {
                             mCb.onTaskLaunchFailed();
                         }
@@ -648,37 +702,15 @@
     }
 
     @Override
-    public void onTaskViewAppInfoClicked(Task t) {
-        // Create a new task stack with the application info details activity
-        Intent baseIntent = t.key.baseIntent;
-        Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
-                Uri.fromParts("package", baseIntent.getComponent().getPackageName(), null));
-        intent.setComponent(intent.resolveActivity(getContext().getPackageManager()));
-        TaskStackBuilder.create(getContext())
-                .addNextIntentWithParentStack(intent).startActivities(null,
-                new UserHandle(t.key.userId));
-    }
-
-    @Override
-    public void onTaskViewDismissed(Task t) {
-        // Remove any stored data from the loader.  We currently don't bother notifying the views
-        // that the data has been unloaded because at the point we call onTaskViewDismissed(), the views
-        // either don't need to be updated, or have already been removed.
-        RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
-        loader.deleteTaskData(t, false);
-
-        // Remove the old task from activity manager
-        loader.getSystemServicesProxy().removeTask(t.key.id);
-    }
-
-    @Override
     public void onAllTaskViewsDismissed(ArrayList<Task> removedTasks) {
+        /* TODO: Not currently enabled
         if (removedTasks != null) {
             int taskCount = removedTasks.size();
             for (int i = 0; i < taskCount; i++) {
                 onTaskViewDismissed(removedTasks.get(i));
             }
         }
+        */
 
         mCb.onAllTaskViewsDismissed();
 
@@ -726,20 +758,63 @@
         }
     }
 
-    @Override
-    public void onTaskResize(Task t) {
-        if (mCb != null) {
-            mCb.onTaskResize(t);
-        }
+    /**** EventBus Events ****/
+
+    public final void onBusEvent(DragStartEvent event) {
+        // Add the drag view
+        mDragView = event.dragView;
+        addView(mDragView);
     }
 
-    /**** RecentsPackageMonitor.PackageCallbacks Implementation ****/
+    public final void onBusEvent(DragDockStateChangedEvent event) {
+        // Update the task stack positions, and then
+        if (event.dockState != null) {
+            // Draw an overlay on the bounds of the dock task
+            mDockRegionOverlay.setBounds(
+                    event.dockState.getDockedBounds(getMeasuredWidth(), getMeasuredHeight()));
+            mDockRegionOverlay.setAlpha(255);
+        } else {
+            mDockRegionOverlay.setAlpha(0);
+        }
+        invalidate();
+    }
 
-    @Override
-    public void onPackagesChanged(RecentsPackageMonitor monitor, String packageName, int userId) {
-        // Propagate this event down to each task stack view
-        if (mTaskStackView != null) {
-            mTaskStackView.onPackagesChanged(monitor, packageName, userId);
+    public final void onBusEvent(final DragEndEvent event) {
+        event.postAnimationTrigger.increment();
+        event.postAnimationTrigger.addLastDecrementRunnable(new Runnable() {
+            @Override
+            public void run() {
+                // Remove the drag view
+                removeView(mDragView);
+                mDragView = null;
+                mDockRegionOverlay.setAlpha(0);
+                invalidate();
+
+                // Dock the new task if we are hovering over a valid dock state
+                if (event.dockState != null) {
+                    SystemServicesProxy ssp = RecentsTaskLoader.getInstance().getSystemServicesProxy();
+                    ssp.setTaskResizeable(event.task.key.id);
+                    ssp.dockTask(event.task.key.id, event.dockState.createMode);
+                    launchTask(event.task, null);
+                }
+            }
+        });
+        if (event.dockState == null) {
+            // Animate the alpha back to what it was before
+            Rect taskBounds = mTaskStackView.getStackAlgorithm().getUntransformedTaskViewBounds();
+            int left = taskBounds.left + (int) ((1f - event.taskView.getScaleX()) * taskBounds.width()) / 2;
+            int top = taskBounds.top + (int) ((1f - event.taskView.getScaleY()) * taskBounds.height()) / 2;
+            event.dragView.animate()
+                    .alpha(1f)
+                    .translationX(left + event.taskView.getTranslationX())
+                    .translationY(top + event.taskView.getTranslationY())
+                    .setDuration(175)
+                    .setInterpolator(new AccelerateInterpolator(1.5f))
+                    .withEndAction(event.postAnimationTrigger.decrementAsRunnable())
+                    .withLayer()
+                    .start();
+        } else {
+            event.postAnimationTrigger.decrement();
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
new file mode 100644
index 0000000..8dea0cd
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
@@ -0,0 +1,149 @@
+/*
+ * 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.recents.views;
+
+import android.content.res.Configuration;
+import android.graphics.Point;
+import android.view.MotionEvent;
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.events.ui.dragndrop.DragDockStateChangedEvent;
+import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent;
+import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent;
+import com.android.systemui.recents.misc.ReferenceCountedTrigger;
+import com.android.systemui.recents.model.Task;
+import com.android.systemui.recents.model.TaskStack;
+
+
+/**
+ * Represents the dock regions for each orientation.
+ */
+class DockRegion {
+    public static TaskStack.DockState[] LANDSCAPE = {
+            TaskStack.DockState.LEFT, TaskStack.DockState.RIGHT
+    };
+    public static TaskStack.DockState[] PORTRAIT = {
+            TaskStack.DockState.TOP, TaskStack.DockState.BOTTOM
+    };
+}
+
+/**
+ * Handles touch events for a RecentsView.
+ */
+class RecentsViewTouchHandler {
+
+    private RecentsView mRv;
+
+    private Task mDragTask;
+    private TaskView mTaskView;
+    private DragView mDragView;
+
+    private Point mDownPos = new Point();
+    private boolean mDragging;
+    private TaskStack.DockState mLastDockState;
+
+    public RecentsViewTouchHandler(RecentsView rv) {
+        mRv = rv;
+    }
+
+    /** Touch preprocessing for handling below */
+    public boolean onInterceptTouchEvent(MotionEvent ev) {
+        int action = ev.getAction();
+        switch (action & MotionEvent.ACTION_MASK) {
+            case MotionEvent.ACTION_DOWN:
+                mDownPos.set((int) ev.getX(), (int) ev.getY());
+                break;
+        }
+        return mDragging;
+    }
+
+    /** Handles touch events once we have intercepted them */
+    public boolean onTouchEvent(MotionEvent ev) {
+        if (!mDragging) return false;
+
+        boolean isLandscape = mRv.getResources().getConfiguration().orientation ==
+                Configuration.ORIENTATION_LANDSCAPE;
+        int action = ev.getAction();
+        switch (action & MotionEvent.ACTION_MASK) {
+            case MotionEvent.ACTION_DOWN:
+                mDownPos.set((int) ev.getX(), (int) ev.getY());
+                break;
+            case MotionEvent.ACTION_MOVE: {
+                int width = mRv.getMeasuredWidth();
+                int height = mRv.getMeasuredHeight();
+                float evX = ev.getX();
+                float evY = ev.getY();
+                float x = evX - mDragView.getTopLeftOffset().x;
+                float y = evY - mDragView.getTopLeftOffset().y;
+
+                // Update the dock state
+                TaskStack.DockState[] dockStates = isLandscape ?
+                        DockRegion.LANDSCAPE : DockRegion.PORTRAIT;
+                TaskStack.DockState foundDockState = null;
+                for (int i = 0; i < dockStates.length; i++) {
+                    TaskStack.DockState state = dockStates[i];
+                    if (state.touchAreaContainsPoint(width, height, evX, evY)) {
+                        foundDockState = state;
+                        break;
+                    }
+                }
+                if (mLastDockState != foundDockState) {
+                    mLastDockState = foundDockState;
+                    EventBus.getDefault().send(new DragDockStateChangedEvent(mDragTask,
+                            foundDockState));
+                }
+
+                mDragView.setTranslationX(x);
+                mDragView.setTranslationY(y);
+                break;
+            }
+            case MotionEvent.ACTION_UP:
+            case MotionEvent.ACTION_CANCEL: {
+                ReferenceCountedTrigger postAnimationTrigger = new ReferenceCountedTrigger(
+                        mRv.getContext(), null, null, null);
+                postAnimationTrigger.increment();
+                EventBus.getDefault().send(new DragEndEvent(mDragTask, mTaskView, mDragView,
+                        mLastDockState, postAnimationTrigger));
+                postAnimationTrigger.decrement();
+                break;
+            }
+        }
+        return true;
+    }
+
+    /**** Events ****/
+
+    public final void onBusEvent(DragStartEvent event) {
+        mRv.getParent().requestDisallowInterceptTouchEvent(true);
+        mDragging = true;
+        mDragTask = event.task;
+        mTaskView = event.taskView;
+        mDragView = event.dragView;
+
+        float x = mDownPos.x - mDragView.getTopLeftOffset().x;
+        float y = mDownPos.y - mDragView.getTopLeftOffset().y;
+        mDragView.setTranslationX(x);
+        mDragView.setTranslationY(y);
+    }
+
+    public final void onBusEvent(DragEndEvent event) {
+        mDragging = false;
+        mDragTask = null;
+        mTaskView = null;
+        mDragView = null;
+        mLastDockState = null;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java b/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java
index 7ce50d8..b28cc21 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java
@@ -22,7 +22,6 @@
 import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
 import com.android.systemui.R;
-import com.android.systemui.recents.RecentsActivity;
 import com.android.systemui.recents.RecentsActivityLaunchState;
 import com.android.systemui.recents.RecentsConfiguration;
 
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 b5f29a0..78b9862 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -29,15 +29,17 @@
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.widget.FrameLayout;
-import com.android.internal.logging.MetricsLogger;
 import com.android.systemui.R;
 import com.android.systemui.recents.Constants;
+import com.android.systemui.recents.RecentsActivity;
 import com.android.systemui.recents.RecentsActivityLaunchState;
 import com.android.systemui.recents.RecentsConfiguration;
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.events.activity.PackagesChangedEvent;
+import com.android.systemui.recents.events.ui.DismissTaskEvent;
 import com.android.systemui.recents.misc.DozeTrigger;
 import com.android.systemui.recents.misc.SystemServicesProxy;
 import com.android.systemui.recents.misc.Utilities;
-import com.android.systemui.recents.model.RecentsPackageMonitor;
 import com.android.systemui.recents.model.RecentsTaskLoader;
 import com.android.systemui.recents.model.Task;
 import com.android.systemui.recents.model.TaskStack;
@@ -54,19 +56,15 @@
 /* The visual representation of a task stack view */
 public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCallbacks,
         TaskView.TaskViewCallbacks, TaskStackViewScroller.TaskStackViewScrollerCallbacks,
-        ViewPool.ViewPoolConsumer<TaskView, Task>, RecentsPackageMonitor.PackageCallbacks {
+        ViewPool.ViewPoolConsumer<TaskView, Task> {
 
     /** The TaskView callbacks */
     interface TaskStackViewCallbacks {
         public void onTaskViewClicked(TaskStackView stackView, TaskView tv, TaskStack stack, Task t,
                                       boolean lockToTask, boolean boundsValid, Rect bounds);
-        public void onTaskViewAppInfoClicked(Task t);
-        public void onTaskViewDismissed(Task t);
         public void onAllTaskViewsDismissed(ArrayList<Task> removedTasks);
         public void onTaskStackFilterTriggered();
         public void onTaskStackUnfilterTriggered();
-
-        public void onTaskResize(Task t);
     }
     RecentsConfiguration mConfig;
 
@@ -77,7 +75,7 @@
     TaskStackViewTouchHandler mTouchHandler;
     TaskStackViewCallbacks mCb;
     ViewPool<TaskView, Task> mViewPool;
-    ArrayList<TaskViewTransform> mCurrentTaskTransforms = new ArrayList<TaskViewTransform>();
+    ArrayList<TaskViewTransform> mCurrentTaskTransforms = new ArrayList<>();
     DozeTrigger mUIDozeTrigger;
     DismissView mDismissAllButton;
     boolean mDismissAllButtonAnimating;
@@ -97,9 +95,9 @@
     Matrix mTmpMatrix = new Matrix();
     Rect mTmpRect = new Rect();
     TaskViewTransform mTmpTransform = new TaskViewTransform();
-    HashMap<Task, TaskView> mTmpTaskViewMap = new HashMap<Task, TaskView>();
-    ArrayList<TaskView> mTaskViews = new ArrayList<TaskView>();
-    List<TaskView> mImmutableTaskViews = new ArrayList<TaskView>();
+    HashMap<Task, TaskView> mTmpTaskViewMap = new HashMap<>();
+    ArrayList<TaskView> mTaskViews = new ArrayList<>();
+    List<TaskView> mImmutableTaskViews = new ArrayList<>();
     LayoutInflater mInflater;
     boolean mLayersDisabled;
 
@@ -147,6 +145,18 @@
         mCb = cb;
     }
 
+    @Override
+    protected void onAttachedToWindow() {
+        EventBus.getDefault().register(this, RecentsActivity.EVENT_BUS_PRIORITY + 1);
+        super.onAttachedToWindow();
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        EventBus.getDefault().unregister(this);
+    }
+
     /** Sets the task stack */
     void setStack(TaskStack stack) {
         // Set the new stack
@@ -1161,11 +1171,6 @@
             // Fade the dismiss button back in
             showDismissAllButton();
         }
-
-        // Notify the callback that we've removed the task and it can clean up after it. Note, we
-        // do this after onAllTaskViewsDismissed() is called, to allow the home activity to be
-        // started before the call to remove the task.
-        mCb.onTaskViewDismissed(removedTask);
     }
 
     @Override
@@ -1291,9 +1296,7 @@
         RecentsTaskLoader.getInstance().loadTaskData(task);
 
         // If the doze trigger has already fired, then update the state for this task view
-        if (mUIDozeTrigger.hasTriggered()) {
-            tv.setNoUserInteractionState();
-        }
+        tv.setNoUserInteractionState();
 
         // If we've finished the start animation, then ensure we always enable the focus animations
         if (mStartEnterAnimationCompleted) {
@@ -1343,27 +1346,6 @@
     /**** TaskViewCallbacks Implementation ****/
 
     @Override
-    public void onTaskViewAppIconClicked(TaskView tv) {
-        if (Constants.DebugFlags.App.EnableTaskFiltering) {
-            if (mStack.hasFilteredTasks()) {
-                mStack.unfilterTasks();
-            } else {
-                mStack.filterTasks(tv.getTask());
-            }
-        }
-    }
-
-    @Override
-    public void onTaskViewAppInfoClicked(TaskView tv) {
-        if (mCb != null) {
-            mCb.onTaskViewAppInfoClicked(tv.getTask());
-
-            // Keep track of app-info invocations
-            MetricsLogger.count(getContext(), "overview_app_info", 1);
-        }
-    }
-
-    @Override
     public void onTaskViewClicked(TaskView tv, Task task, boolean lockToTask) {
         // Cancel any doze triggers
         mUIDozeTrigger.stopDozing();
@@ -1374,33 +1356,6 @@
     }
 
     @Override
-    public void onTaskViewDismissed(TaskView tv) {
-        Task task = tv.getTask();
-        int taskIndex = mStack.indexOfTask(task);
-        boolean taskWasFocused = tv.isFocusedTask();
-        // Announce for accessibility
-        tv.announceForAccessibility(getContext().getString(R.string.accessibility_recents_item_dismissed,
-                tv.getTask().activityLabel));
-        // Remove the task from the view
-        mStack.removeTask(task);
-        // If the dismissed task was focused, then we should focus the new task in the same index
-        if (taskWasFocused) {
-            ArrayList<Task> tasks = mStack.getTasks();
-            int nextTaskIndex = Math.min(tasks.size() - 1, taskIndex - 1);
-            if (nextTaskIndex >= 0) {
-                Task nextTask = tasks.get(nextTaskIndex);
-                TaskView nextTv = getChildViewForTask(nextTask);
-                if (nextTv != null) {
-                    // Focus the next task, and only animate the visible state if we are launched
-                    // from Alt-Tab
-                    RecentsActivityLaunchState launchState = mConfig.getLaunchState();
-                    nextTv.setFocusedTask(launchState.launchedWithAltTab);
-                }
-            }
-        }
-    }
-
-    @Override
     public void onTaskViewClipStateChanged(TaskView tv) {
         if (!mStackViewsDirty) {
             invalidate();
@@ -1414,13 +1369,6 @@
         }
     }
 
-    @Override
-    public void onTaskResize(TaskView tv) {
-        if (mCb != null) {
-            mCb.onTaskResize(tv.getTask());
-        }
-    }
-
     /**** TaskStackViewScroller.TaskStackViewScrollerCallbacks ****/
 
     @Override
@@ -1430,13 +1378,12 @@
         postInvalidateOnAnimation();
     }
 
-    /**** RecentsPackageMonitor.PackageCallbacks Implementation ****/
+    /**** EventBus Events ****/
 
-    @Override
-    public void onPackagesChanged(RecentsPackageMonitor monitor, String packageName, int userId) {
+    public final void onBusEvent(PackagesChangedEvent event) {
         // Compute which components need to be removed
-        HashSet<ComponentName> removedComponents = monitor.computeComponentsRemoved(
-                mStack.getTaskKeys(), packageName, userId);
+        HashSet<ComponentName> removedComponents = event.monitor.computeComponentsRemoved(
+                mStack.getTaskKeys(), event.packageName, event.userId);
 
         // For other tasks, just remove them directly if they no longer exist
         ArrayList<Task> tasks = mStack.getTasks();
@@ -1459,4 +1406,33 @@
             }
         }
     }
+
+    public final void onBusEvent(DismissTaskEvent event) {
+        TaskView tv = event.taskView;
+        Task task = tv.getTask();
+        int taskIndex = mStack.indexOfTask(task);
+        boolean taskWasFocused = tv.isFocusedTask();
+
+        // Announce for accessibility
+        tv.announceForAccessibility(getContext().getString(R.string.accessibility_recents_item_dismissed,
+                tv.getTask().activityLabel));
+
+        // Remove the task from the view
+        mStack.removeTask(task);
+        // If the dismissed task was focused, then we should focus the new task in the same index
+        if (taskWasFocused) {
+            ArrayList<Task> tasks = mStack.getTasks();
+            int nextTaskIndex = Math.min(tasks.size() - 1, taskIndex - 1);
+            if (nextTaskIndex >= 0) {
+                Task nextTask = tasks.get(nextTaskIndex);
+                TaskView nextTv = getChildViewForTask(nextTask);
+                if (nextTv != null) {
+                    // Focus the next task, and only animate the visible state if we are launched
+                    // from Alt-Tab
+                    RecentsActivityLaunchState launchState = mConfig.getLaunchState();
+                    nextTv.setFocusedTask(launchState.launchedWithAltTab);
+                }
+            }
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewFilterAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewFilterAlgorithm.java
index e9f6a46..a32b242 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewFilterAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewFilterAlgorithm.java
@@ -16,9 +16,9 @@
 
 package com.android.systemui.recents.views;
 
+import com.android.systemui.R;
 import com.android.systemui.recents.Constants;
 import com.android.systemui.recents.model.Task;
-import com.android.systemui.R;
 
 import java.util.ArrayList;
 import java.util.HashMap;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java
index 7f4c0a5..a8e6f47 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java
@@ -18,11 +18,11 @@
 
 import android.content.Context;
 import android.graphics.Rect;
+import com.android.systemui.R;
 import com.android.systemui.recents.Constants;
 import com.android.systemui.recents.RecentsConfiguration;
 import com.android.systemui.recents.misc.Utilities;
 import com.android.systemui.recents.model.Task;
-import com.android.systemui.R;
 
 import java.util.ArrayList;
 import java.util.HashMap;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
index f0ae87f..e4fbc76 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
@@ -24,8 +24,8 @@
 import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
 import android.widget.OverScroller;
-import com.android.systemui.recents.misc.Utilities;
 import com.android.systemui.R;
+import com.android.systemui.recents.misc.Utilities;
 
 /* The scrolling logic for a TaskStackView */
 public class TaskStackViewScroller {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
index 86eced8..48c5b46 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
@@ -24,9 +24,11 @@
 import android.view.ViewConfiguration;
 import android.view.ViewParent;
 import com.android.internal.logging.MetricsLogger;
+import com.android.systemui.R;
 import com.android.systemui.recents.Constants;
 import com.android.systemui.recents.Recents;
-import com.android.systemui.R;
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.events.ui.DismissTaskEvent;
 
 import java.util.List;
 
@@ -435,7 +437,7 @@
         // Re-enable touch events from this task view
         tv.setTouchEnabled(true);
         // Remove the task view from the stack
-        mSv.onTaskViewDismissed(tv);
+        EventBus.getDefault().send(new DismissTaskEvent(tv.getTask(), tv));
         // Keep track of deletions by keyboard
         MetricsLogger.histogram(tv.getContext(), "overview_task_dismissed_source",
                 Constants.Metrics.DismissSourceSwipeGesture);
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 bbbaccf..9d08ee9 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -21,26 +21,35 @@
 import android.animation.ValueAnimator;
 import android.content.Context;
 import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Outline;
 import android.graphics.Paint;
+import android.graphics.Point;
 import android.graphics.PorterDuff;
 import android.graphics.PorterDuffColorFilter;
 import android.graphics.Rect;
 import android.util.AttributeSet;
+import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewOutlineProvider;
-import android.view.accessibility.AccessibilityManager;
 import android.view.animation.AccelerateInterpolator;
 import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
 import android.widget.FrameLayout;
-import com.android.internal.logging.MetricsLogger;
 import com.android.systemui.R;
 import com.android.systemui.recents.Constants;
+import com.android.systemui.recents.RecentsActivity;
 import com.android.systemui.recents.RecentsActivityLaunchState;
 import com.android.systemui.recents.RecentsConfiguration;
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.events.ui.DismissTaskEvent;
+import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent;
+import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent;
+import com.android.systemui.recents.misc.SystemServicesProxy;
 import com.android.systemui.recents.misc.Utilities;
+import com.android.systemui.recents.model.RecentsTaskLoader;
 import com.android.systemui.recents.model.Task;
 import com.android.systemui.statusbar.phone.PhoneStatusBar;
 
@@ -50,14 +59,9 @@
 
     /** The TaskView callbacks */
     interface TaskViewCallbacks {
-        public void onTaskViewAppIconClicked(TaskView tv);
-        public void onTaskViewAppInfoClicked(TaskView tv);
         public void onTaskViewClicked(TaskView tv, Task task, boolean lockToTask);
-        public void onTaskViewDismissed(TaskView tv);
         public void onTaskViewClipStateChanged(TaskView tv);
         public void onTaskViewFocusChanged(TaskView tv, boolean focused);
-
-        public void onTaskResize(TaskView tv);
     }
 
     RecentsConfiguration mConfig;
@@ -84,6 +88,8 @@
     View mActionButtonView;
     TaskViewCallbacks mCb;
 
+    Point mDownTouchPos = new Point();
+
     Interpolator mFastOutSlowInInterpolator;
     Interpolator mFastOutLinearInInterpolator;
     Interpolator mQuintOutInterpolator;
@@ -174,6 +180,14 @@
     }
 
     @Override
+    public boolean onInterceptTouchEvent(MotionEvent ev) {
+        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+            mDownTouchPos.set((int) (ev.getX() * getScaleX()), (int) (ev.getY() * getScaleY()));
+        }
+        return super.onInterceptTouchEvent(ev);
+    }
+
+    @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         int width = MeasureSpec.getSize(widthMeasureSpec);
         int height = MeasureSpec.getSize(heightMeasureSpec);
@@ -535,9 +549,7 @@
         startDeleteTaskAnimation(new Runnable() {
             @Override
             public void run() {
-                if (mCb != null) {
-                    mCb.onTaskViewDismissed(tv);
-                }
+                EventBus.getDefault().send(new DismissTaskEvent(mTask, tv));
             }
         }, 0);
     }
@@ -615,8 +627,12 @@
 
     /** Compute the dim as a function of the scale of this view. */
     int getDimFromTaskProgress() {
+        // TODO: Temporarily disable the dim on the stack
+        /*
         float dim = mMaxDimScale * mDimInterpolator.getInterpolation(1f - mTaskProgress);
         return (int) (dim * 255);
+        */
+        return 0;
     }
 
     /** Update the dim as a function of the scale of this view. */
@@ -725,17 +741,8 @@
             mThumbnailView.rebindToTask(mTask);
             mHeaderView.rebindToTask(mTask);
             // Rebind any listeners
-            AccessibilityManager am = (AccessibilityManager) getContext().
-                    getSystemService(Context.ACCESSIBILITY_SERVICE);
-            if (Constants.DebugFlags.App.EnableTaskFiltering || (am != null && am.isEnabled())) {
-                mHeaderView.mApplicationIcon.setOnClickListener(this);
-            }
-            mHeaderView.mDismissButton.setOnClickListener(this);
-            if (mConfig.multiWindowEnabled) {
-                mHeaderView.mMoveTaskButton.setOnClickListener(this);
-            }
             mActionButtonView.setOnClickListener(this);
-            mHeaderView.mApplicationIcon.setOnLongClickListener(this);
+            setOnLongClickListener(mConfig.hasDockedTasks ? null : this);
         }
         mTaskDataLoaded = true;
     }
@@ -748,13 +755,7 @@
             mThumbnailView.unbindFromTask();
             mHeaderView.unbindFromTask();
             // Unbind any listeners
-            mHeaderView.mApplicationIcon.setOnClickListener(null);
-            mHeaderView.mDismissButton.setOnClickListener(null);
-            if (mConfig.multiWindowEnabled) {
-                mHeaderView.mMoveTaskButton.setOnClickListener(null);
-            }
             mActionButtonView.setOnClickListener(null);
-            mHeaderView.mApplicationIcon.setOnLongClickListener(null);
         }
         mTaskDataLoaded = false;
     }
@@ -768,47 +769,12 @@
 
     @Override
      public void onClick(final View v) {
-        final TaskView tv = this;
-        final boolean delayViewClick = (v != this) && (v != mActionButtonView);
-        if (delayViewClick) {
-            // We purposely post the handler delayed to allow for the touch feedback to draw
-            postDelayed(new Runnable() {
-                @Override
-                public void run() {
-                    if (v == mHeaderView.mApplicationIcon) {
-                        if (Constants.DebugFlags.App.EnableTaskFiltering) {
-                            if (mCb != null) {
-                                mCb.onTaskViewAppIconClicked(tv);
-                            }
-                        } else {
-                            AccessibilityManager am = (AccessibilityManager) getContext().
-                                    getSystemService(Context.ACCESSIBILITY_SERVICE);
-                            if (am != null && am.isEnabled()) {
-                                if (mCb != null) {
-                                    mCb.onTaskViewAppInfoClicked(tv);
-                                }
-                            }
-                        }
-                    } else if (v == mHeaderView.mDismissButton) {
-                        dismissTask();
-                        // Keep track of deletions by the dismiss button
-                        MetricsLogger.histogram(getContext(), "overview_task_dismissed_source",
-                                Constants.Metrics.DismissSourceHeaderButton);
-                    } else if (v == mHeaderView.mMoveTaskButton) {
-                        if (mCb != null) {
-                            mCb.onTaskResize(tv);
-                        }
-                    }
-                }
-            }, 125);
-        } else {
-            if (v == mActionButtonView) {
-                // Reset the translation of the action button before we animate it out
-                mActionButtonView.setTranslationZ(0f);
-            }
-            if (mCb != null) {
-                mCb.onTaskViewClicked(tv, tv.getTask(), (v == mActionButtonView));
-            }
+        if (v == mActionButtonView) {
+            // Reset the translation of the action button before we animate it out
+            mActionButtonView.setTranslationZ(0f);
+        }
+        if (mCb != null) {
+            mCb.onTaskViewClicked(this, mTask, (v == mActionButtonView));
         }
     }
 
@@ -816,12 +782,64 @@
 
     @Override
     public boolean onLongClick(View v) {
-        if (v == mHeaderView.mApplicationIcon) {
-            if (mCb != null) {
-                mCb.onTaskViewAppInfoClicked(this);
-                return true;
-            }
+        if (v == this) {
+            // Start listening for drag events
+            setClipViewInStack(false);
+
+            int width = (int) (getScaleX() * getWidth());
+            int height = (int) (getScaleY() * getHeight());
+            Bitmap dragBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+            Canvas c = new Canvas(dragBitmap);
+            c.scale(getScaleX(), getScaleY());
+            mThumbnailView.draw(c);
+            mHeaderView.draw(c);
+            c.setBitmap(null);
+
+            // Initiate the drag
+            final DragView dragView = new DragView(getContext(), dragBitmap, mDownTouchPos);
+            dragView.setOutlineProvider(mViewBounds);
+            dragView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
+                @Override
+                public void onViewAttachedToWindow(View v) {
+                    // Hide this task view after the drag view is attached
+                    setVisibility(View.INVISIBLE);
+                    // Animate the alpha slightly to indicate dragging
+                    dragView.setElevation(getElevation());
+                    dragView.setTranslationZ(getTranslationZ());
+                    dragView.animate()
+                            .alpha(0.75f)
+                            .setDuration(175)
+                            .setInterpolator(new AccelerateInterpolator(1.5f))
+                            .withLayer()
+                            .start();
+                }
+
+                @Override
+                public void onViewDetachedFromWindow(View v) {
+                }
+            });
+            EventBus.getDefault().register(this, RecentsActivity.EVENT_BUS_PRIORITY + 1);
+            EventBus.getDefault().send(new DragStartEvent(mTask, this, dragView));
+            return true;
         }
         return false;
     }
+
+    /**** Events ****/
+
+    public final void onBusEvent(DragEndEvent event) {
+        event.postAnimationTrigger.addLastDecrementRunnable(new Runnable() {
+            @Override
+            public void run() {
+                // If docked state == null:
+                // Animate the drag view back from where it is, to the view location, then after it returns,
+                // update the clip state
+                setClipViewInStack(true);
+
+                // Show this task view
+                setVisibility(View.VISIBLE);
+            }
+        });
+        EventBus.getDefault().unregister(this);
+    }
 }
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 f68dd64..949d515 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
@@ -39,14 +39,19 @@
 import android.util.AttributeSet;
 import android.view.View;
 import android.view.ViewOutlineProvider;
+import android.view.accessibility.AccessibilityManager;
 import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
 import android.widget.FrameLayout;
 import android.widget.ImageView;
 import android.widget.TextView;
+import com.android.internal.logging.MetricsLogger;
 import com.android.systemui.R;
 import com.android.systemui.recents.Constants;
 import com.android.systemui.recents.RecentsConfiguration;
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.events.ui.ResizeTaskEvent;
+import com.android.systemui.recents.events.ui.ShowApplicationInfoEvent;
 import com.android.systemui.recents.misc.SystemServicesProxy;
 import com.android.systemui.recents.misc.Utilities;
 import com.android.systemui.recents.model.RecentsTaskLoader;
@@ -54,10 +59,12 @@
 
 
 /* The task bar view */
-public class TaskViewHeader extends FrameLayout {
+public class TaskViewHeader extends FrameLayout
+        implements View.OnClickListener, View.OnLongClickListener {
 
     RecentsConfiguration mConfig;
     private SystemServicesProxy mSsp;
+    Task mTask;
 
     // Header views
     ImageView mMoveTaskButton;
@@ -144,15 +151,15 @@
     protected void onFinishInflate() {
         // Initialize the icon and description views
         mApplicationIcon = (ImageView) findViewById(R.id.application_icon);
+        mApplicationIcon.setOnLongClickListener(this);
         mActivityDescription = (TextView) findViewById(R.id.activity_description);
         mDismissButton = (ImageView) findViewById(R.id.dismiss_task);
+        mDismissButton.setOnClickListener(this);
         mMoveTaskButton = (ImageView) findViewById(R.id.move_task);
 
         // Hide the backgrounds if they are ripple drawables
-        if (!Constants.DebugFlags.App.EnableTaskFiltering) {
-            if (mApplicationIcon.getBackground() instanceof RippleDrawable) {
-                mApplicationIcon.setBackground(null);
-            }
+        if (mApplicationIcon.getBackground() instanceof RippleDrawable) {
+            mApplicationIcon.setBackground(null);
         }
 
         mBackgroundColorDrawable = (GradientDrawable) getContext().getDrawable(R.drawable
@@ -203,6 +210,8 @@
 
     /** Binds the bar view to the task */
     public void rebindToTask(Task t) {
+        mTask = t;
+
         // If an activity icon is defined, then we use that as the primary icon to show in the bar,
         // otherwise, we fall back to the application icon
         if (t.activityIcon != null) {
@@ -235,12 +244,26 @@
                 mLightDismissDrawable : mDarkDismissDrawable);
         mDismissButton.setContentDescription(String.format(mDismissContentDescription,
                 t.contentDescription));
-        mMoveTaskButton.setVisibility((mConfig.multiWindowEnabled) ? View.VISIBLE : View.INVISIBLE);
-        if (mConfig.multiWindowEnabled) {
-            updateResizeTaskBarIcon(t);
+        updateResizeTaskBarIcon(t);
+        mMoveTaskButton.setVisibility(View.VISIBLE);
+        mMoveTaskButton.setOnClickListener(this);
+
+        // In accessibility, a single click on the focused app info button will show it
+        AccessibilityManager am = (AccessibilityManager) getContext().
+                getSystemService(Context.ACCESSIBILITY_SERVICE);
+        if (am != null && am.isEnabled()) {
+            mApplicationIcon.setOnClickListener(this);
         }
     }
 
+    /** Unbinds the bar view from the task */
+    void unbindFromTask() {
+        mTask = null;
+        mApplicationIcon.setImageDrawable(null);
+        mApplicationIcon.setOnClickListener(null);
+        mMoveTaskButton.setOnClickListener(null);
+    }
+
     /** Updates the resize task bar button. */
     void updateResizeTaskBarIcon(Task t) {
         Rect display = mSsp.getWindowRect();
@@ -274,11 +297,6 @@
         mMoveTaskButton.setImageResource(resId);
     }
 
-    /** Unbinds the bar view from the task */
-    void unbindFromTask() {
-        mApplicationIcon.setImageDrawable(null);
-    }
-
     /** Animates this task bar dismiss button when launching a task. */
     void startLaunchTaskDismissAnimation() {
         if (mDismissButton.getVisibility() == View.VISIBLE) {
@@ -441,4 +459,30 @@
             }
         }
     }
+
+    @Override
+    public void onClick(View v) {
+        if (v == mApplicationIcon) {
+            // In accessibility, a single click on the focused app info button will show it
+            EventBus.getDefault().send(new ShowApplicationInfoEvent(mTask));
+        } else if (v == mDismissButton) {
+            TaskView tv = Utilities.findParent(this, TaskView.class);
+            tv.dismissTask();
+
+            // Keep track of deletions by the dismiss button
+            MetricsLogger.histogram(getContext(), "overview_task_dismissed_source",
+                    Constants.Metrics.DismissSourceHeaderButton);
+        } else if (v == mMoveTaskButton) {
+            EventBus.getDefault().send(new ResizeTaskEvent(mTask));
+        }
+    }
+
+    @Override
+    public boolean onLongClick(View v) {
+        if (v == mApplicationIcon) {
+            EventBus.getDefault().send(new ShowApplicationInfoEvent(mTask));
+            return true;
+        }
+        return false;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
index 6c83bee..690c297 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
@@ -34,9 +34,9 @@
 import android.view.View;
 import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
+import com.android.systemui.R;
 import com.android.systemui.recents.misc.Utilities;
 import com.android.systemui.recents.model.Task;
-import com.android.systemui.R;
 
 
 /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index e7a3c8a..aaed735 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -1539,6 +1539,7 @@
             if (viableAction != null) {
                 Notification stripped = n.clone();
                 Notification.Builder.stripForDelivery(stripped);
+                stripped.extras.putBoolean("android.rebuild", true);
                 stripped.actions = new Notification.Action[] { viableAction };
                 stripped.extras.putBoolean("android.rebuild.contentView", true);
                 stripped.contentView = null;
@@ -1547,6 +1548,13 @@
                 stripped.extras.putBoolean("android.rebuild.hudView", true);
                 stripped.headsUpContentView = null;
 
+                stripped.extras.putParcelable(Notification.EXTRA_LARGE_ICON,
+                        stripped.getLargeIcon());
+                if (SystemProperties.getBoolean("debug.strip_third_line", false)) {
+                    stripped.extras.putCharSequence(Notification.EXTRA_INFO_TEXT, null);
+                    stripped.extras.putCharSequence(Notification.EXTRA_SUMMARY_TEXT, null);
+                }
+
                 Notification rebuilt = Notification.Builder.rebuild(mContext, stripped);
 
                 n.actions = rebuilt.actions;
@@ -1580,27 +1588,35 @@
         if (remoteInput != null) {
             View bigContentView = entry.getExpandedContentView();
             if (bigContentView != null) {
-                inflateRemoteInput(bigContentView, remoteInput, actions);
+                inflateRemoteInput(bigContentView, entry, remoteInput, actions);
             }
             View headsUpContentView = entry.getHeadsUpContentView();
             if (headsUpContentView != null) {
-                inflateRemoteInput(headsUpContentView, remoteInput, actions);
+                inflateRemoteInput(headsUpContentView, entry, remoteInput, actions);
             }
         }
 
     }
 
-    private void inflateRemoteInput(View view, RemoteInput remoteInput,
+    private void inflateRemoteInput(View view, Entry entry, RemoteInput remoteInput,
             Notification.Action[] actions) {
         View actionContainerCandidate = view.findViewById(com.android.internal.R.id.actions);
         if (actionContainerCandidate instanceof ViewGroup) {
             ViewGroup actionContainer = (ViewGroup) actionContainerCandidate;
-            actionContainer.removeAllViews();
-            actionContainer.addView(
-                    RemoteInputView.inflate(mContext, actionContainer, actions[0], remoteInput));
+            RemoteInputView riv = inflateRemoteInputView(actionContainer, entry,
+                    actions[0], remoteInput);
+            if (riv != null) {
+                actionContainer.removeAllViews();
+                actionContainer.addView(riv);
+            }
         }
     }
 
+    protected RemoteInputView inflateRemoteInputView(ViewGroup root, Entry entry,
+            Notification.Action action, RemoteInput remoteInput) {
+        return null;
+    }
+
     private final class NotificationClicker implements View.OnClickListener {
         public void onClick(final View v) {
             if (!(v instanceof ExpandableNotificationRow)) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java b/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java
new file mode 100644
index 0000000..f243b00
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java
@@ -0,0 +1,102 @@
+/*
+ * 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 com.android.internal.util.Preconditions;
+import com.android.systemui.statusbar.phone.StatusBarWindowManager;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
+import com.android.systemui.statusbar.policy.RemoteInputView;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+
+/**
+ * Keeps track of the currently active {@link RemoteInputView}s.
+ */
+public class RemoteInputController {
+
+    private final ArrayList<WeakReference<NotificationData.Entry>> mRemoteInputs = new ArrayList<>();
+    private final StatusBarWindowManager mStatusBarWindowManager;
+    private final HeadsUpManager mHeadsUpManager;
+
+    public RemoteInputController(StatusBarWindowManager sbwm, HeadsUpManager headsUpManager) {
+        mStatusBarWindowManager = sbwm;
+        mHeadsUpManager = headsUpManager;
+    }
+
+    public void addRemoteInput(NotificationData.Entry entry) {
+        Preconditions.checkNotNull(entry);
+
+        boolean found = pruneWeakThenRemoveAndContains(
+                entry /* contains */, null /* remove */);
+        if (!found) {
+            mRemoteInputs.add(new WeakReference<>(entry));
+        }
+
+        apply(entry);
+    }
+
+    public void removeRemoteInput(NotificationData.Entry entry) {
+        Preconditions.checkNotNull(entry);
+
+        pruneWeakThenRemoveAndContains(null /* contains */, entry /* remove */);
+
+        apply(entry);
+    }
+
+    private void apply(NotificationData.Entry entry) {
+        mStatusBarWindowManager.setRemoteInputActive(isRemoteInputActive());
+        mHeadsUpManager.setRemoteInputActive(entry, isRemoteInputActive(entry));
+    }
+
+    /**
+     * @return true if {@param entry} has an active RemoteInput
+     */
+    public boolean isRemoteInputActive(NotificationData.Entry entry) {
+        return pruneWeakThenRemoveAndContains(entry /* contains */, null /* remove */);
+    }
+
+    /**
+     * @return true if any entry has an active RemoteInput
+     */
+    public boolean isRemoteInputActive() {
+        pruneWeakThenRemoveAndContains(null /* contains */, null /* remove */);
+        return !mRemoteInputs.isEmpty();
+    }
+
+    /**
+     * Prunes dangling weak references, removes entries referring to {@param remove} and returns
+     * whether {@param contains} is part of the array in a single loop.
+     * @param remove if non-null, removes this entry from the active remote inputs
+     * @return true if {@param contains} is in the set of active remote inputs
+     */
+    private boolean pruneWeakThenRemoveAndContains(
+            NotificationData.Entry contains, NotificationData.Entry remove) {
+        boolean found = false;
+        for (int i = mRemoteInputs.size() - 1; i >= 0; i--) {
+            NotificationData.Entry item = mRemoteInputs.get(i).get();
+            if (item == null || item == remove) {
+                mRemoteInputs.remove(i);
+            } else if (item == contains) {
+                found = true;
+            }
+        }
+        return found;
+    }
+
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
index f7c3c67..cc30882 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
@@ -250,6 +250,7 @@
     @Override
     public void setNoSims(boolean show) {
         mNoSimsVisible = show && !mBlockMobile;
+        apply();
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BaseStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BaseStatusBarHeader.java
new file mode 100644
index 0000000..497f044
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BaseStatusBarHeader.java
@@ -0,0 +1,46 @@
+/*
+ * 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.phone;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.RelativeLayout;
+import com.android.systemui.qs.QSPanel;
+import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.statusbar.policy.NetworkControllerImpl;
+import com.android.systemui.statusbar.policy.NextAlarmController;
+import com.android.systemui.statusbar.policy.UserInfoController;
+
+public abstract class BaseStatusBarHeader extends RelativeLayout implements
+        NetworkControllerImpl.EmergencyListener {
+
+    public BaseStatusBarHeader(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public abstract int getCollapsedHeight();
+    public abstract int getExpandedHeight();
+    public abstract void setExpanded(boolean b);
+    public abstract void setExpansion(float headerExpansionFraction);
+    public abstract void setListening(boolean listening);
+    public abstract void updateEverything();
+    public abstract void setActivityStarter(ActivityStarter activityStarter);
+    public abstract void setQSPanel(QSPanel qSPanel);
+    public abstract void setBatteryController(BatteryController batteryController);
+    public abstract void setNextAlarmController(NextAlarmController nextAlarmController);
+    public abstract void setUserInfoController(UserInfoController userInfoController);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarApps.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarApps.java
index 364e884..4ac2c31 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarApps.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarApps.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.statusbar.phone;
 
+import static android.app.ActivityManager.INVALID_STACK_ID;
+
 import android.animation.LayoutTransition;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
@@ -742,9 +744,9 @@
 
     private void activateTask(int taskPersistentId) {
         // Launch or bring the activity to front.
-        IActivityManager manager = ActivityManagerNative.getDefault();
+        final IActivityManager iAm = ActivityManagerNative.getDefault();
         try {
-            manager.startActivityFromRecents(taskPersistentId, null /* options */);
+            iAm.startActivityFromRecents(taskPersistentId, INVALID_STACK_ID, null /* options */);
         } catch (RemoteException e) {
             Slog.e(TAG, "Exception when activating a recent task", e);
         } catch (IllegalArgumentException e) {
@@ -810,7 +812,7 @@
                     removeCallbacks(mShowMenuCallback);
                     break;
             }
-            return true;
+            return false;
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 011889a..33e514d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -187,15 +187,6 @@
         mBarTransitions = new NavigationBarTransitions(this);
     }
 
-    @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        ViewRootImpl root = getViewRootImpl();
-        if (root != null) {
-            root.setDrawDuringWindowsAnimating(true);
-        }
-    }
-
     public BarTransitions getBarTransitions() {
         return mBarTransitions;
     }
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 08353cb..3453652 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -92,7 +92,7 @@
     public static final long DOZE_ANIMATION_DURATION = 700;
 
     private KeyguardAffordanceHelper mAfforanceHelper;
-    private StatusBarHeaderView mHeader;
+    private BaseStatusBarHeader mHeader;
     private KeyguardUserSwitcher mKeyguardUserSwitcher;
     private KeyguardStatusBarView mKeyguardStatusBar;
     private QSContainer mQsContainer;
@@ -232,7 +232,7 @@
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
-        mHeader = (StatusBarHeaderView) findViewById(R.id.header);
+        mHeader = (BaseStatusBarHeader) findViewById(R.id.header);
         mHeader.setOnClickListener(this);
         mKeyguardStatusBar = (KeyguardStatusBarView) findViewById(R.id.keyguard_header);
         mKeyguardStatusView = (KeyguardStatusView) findViewById(R.id.keyguard_status_view);
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 37edc28..05e8488 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -25,6 +25,7 @@
 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;
@@ -82,6 +83,7 @@
 import android.view.ThreadedRenderer;
 import android.view.VelocityTracker;
 import android.view.View;
+import android.view.ViewGroup;
 import android.view.ViewGroup.LayoutParams;
 import android.view.ViewStub;
 import android.view.WindowManager;
@@ -128,6 +130,7 @@
 import com.android.systemui.statusbar.NotificationData;
 import com.android.systemui.statusbar.NotificationData.Entry;
 import com.android.systemui.statusbar.NotificationOverflowContainer;
+import com.android.systemui.statusbar.RemoteInputController;
 import com.android.systemui.statusbar.ScrimView;
 import com.android.systemui.statusbar.SignalClusterView;
 import com.android.systemui.statusbar.SpeedBumpView;
@@ -150,6 +153,7 @@
 import com.android.systemui.statusbar.policy.NetworkControllerImpl;
 import com.android.systemui.statusbar.policy.NextAlarmController;
 import com.android.systemui.statusbar.policy.PreviewInflater;
+import com.android.systemui.statusbar.policy.RemoteInputView;
 import com.android.systemui.statusbar.policy.RotationLockControllerImpl;
 import com.android.systemui.statusbar.policy.SecurityControllerImpl;
 import com.android.systemui.statusbar.policy.UserInfoController;
@@ -299,6 +303,8 @@
 
     StatusBarIconController mIconController;
 
+    private RemoteInputController mRemoteInputController;
+
     // expanded notifications
     NotificationPanelView mNotificationPanel; // the sliding/resizing panel within the notification window
     View mExpandedContents;
@@ -308,7 +314,7 @@
     private QSPanel mQSPanel;
 
     // top bar
-    StatusBarHeaderView mHeader;
+    BaseStatusBarHeader mHeader;
     KeyguardStatusBarView mKeyguardStatusBar;
     View mKeyguardStatusView;
     KeyguardBottomAreaView mKeyguardBottomArea;
@@ -800,7 +806,7 @@
         mStatusBarView.setScrimController(mScrimController);
         mDozeScrimController = new DozeScrimController(mScrimController, context);
 
-        mHeader = (StatusBarHeaderView) mStatusBarWindow.findViewById(R.id.header);
+        mHeader = (BaseStatusBarHeader) mStatusBarWindow.findViewById(R.id.header);
         mHeader.setActivityStarter(this);
         mKeyguardStatusBar = (KeyguardStatusBarView) mStatusBarWindow.findViewById(R.id.keyguard_header);
         mKeyguardStatusView = mStatusBarWindow.findViewById(R.id.keyguard_status_view);
@@ -1084,6 +1090,13 @@
         return mStatusBarWindow;
     }
 
+    @Override
+    protected RemoteInputView inflateRemoteInputView(ViewGroup root, Entry entry,
+            Notification.Action action, RemoteInput remoteInput) {
+        return RemoteInputView.inflate(mContext, root, entry, action, remoteInput,
+                mRemoteInputController);
+    }
+
     public int getStatusBarHeight() {
         if (mNaturalBarHeight < 0) {
             final Resources res = mContext.getResources();
@@ -2840,6 +2853,8 @@
     private void addStatusBarWindow() {
         makeStatusBarView();
         mStatusBarWindowManager = new StatusBarWindowManager(mContext);
+        mRemoteInputController = new RemoteInputController(mStatusBarWindowManager,
+                mHeadsUpManager);
         mStatusBarWindowManager.add(mStatusBarWindow, getStatusBarHeight());
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
index e9c4e49..7f5ffaf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
@@ -57,9 +57,9 @@
 /**
  * The view to manage the header area in the expanded status bar.
  */
-public class StatusBarHeaderView extends RelativeLayout implements View.OnClickListener,
+public class StatusBarHeaderView extends BaseStatusBarHeader implements View.OnClickListener,
         BatteryController.BatteryStateChangeCallback, NextAlarmController.NextAlarmChangeCallback,
-        EmergencyListener {
+        EmergencyListener, TunerService.Tunable {
 
     private boolean mExpanded;
     private boolean mListening;
@@ -128,6 +128,8 @@
     private boolean mShowingDetail;
     private boolean mDetailTransitioning;
 
+    private boolean mAllowExpand = true;
+
     public StatusBarHeaderView(Context context, AttributeSet attrs) {
         super(context, attrs);
     }
@@ -232,6 +234,28 @@
         updateClockCollapsedMargin();
     }
 
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        TunerService.get(mContext).addTunable(this, QSPanel.QS_PAGED_PANEL);
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        TunerService.get(mContext).removeTunable(this);
+    }
+
+    @Override
+    public void onTuningChanged(String key, String newValue) {
+        if (QSPanel.QS_PAGED_PANEL.equals(key)) {
+            mAllowExpand = newValue == null || Integer.parseInt(newValue) == 0;
+            if (!mAllowExpand) {
+                setExpanded(false);
+            }
+        }
+    }
+
     private void updateClockCollapsedMargin() {
         Resources res = getResources();
         int padding = res.getDimensionPixelSize(R.dimen.clock_collapsed_bottom_margin);
@@ -290,7 +314,7 @@
     }
 
     public int getExpandedHeight() {
-        return mExpandedHeight;
+        return mAllowExpand ? mExpandedHeight : mCollapsedHeight;
     }
 
     public void setListening(boolean listening) {
@@ -302,6 +326,9 @@
     }
 
     public void setExpanded(boolean expanded) {
+        if (!mAllowExpand) {
+            expanded = false;
+        }
         boolean changed = expanded != mExpanded;
         mExpanded = expanded;
         if (changed) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
index dd62d9b..abe51ac 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
@@ -123,7 +123,7 @@
     private void applyFocusableFlag(State state) {
         boolean panelFocusable = state.statusBarFocusable && state.panelExpanded;
         if (state.keyguardShowing && state.keyguardNeedsInput && state.bouncerShowing
-                || BaseStatusBar.ENABLE_REMOTE_INPUT && panelFocusable) {
+                || BaseStatusBar.ENABLE_REMOTE_INPUT && state.remoteInputActive) {
             mLpChanged.flags &= ~WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
             mLpChanged.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
         } else if (state.isKeyguardShowingAndNotOccluded() || panelFocusable) {
@@ -292,6 +292,11 @@
         apply(mCurrentState);
     }
 
+    public void setRemoteInputActive(boolean remoteInputActive) {
+        mCurrentState.remoteInputActive = remoteInputActive;
+        apply(mCurrentState);
+    }
+
     /**
      * Set whether the screen brightness is forced to the value we use for doze mode by the status
      * bar window.
@@ -326,6 +331,8 @@
          */
         int statusBarState;
 
+        boolean remoteInputActive;
+
         private boolean isKeyguardShowingAndNotOccluded() {
             return keyguardShowing && !keyguardOccluded;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
index cfd3358..35a17e4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -144,13 +144,6 @@
     protected void onAttachedToWindow () {
         super.onAttachedToWindow();
 
-        // We really need to be able to animate while window animations are going on
-        // so that activities may be started asynchronously from panel animations
-        final ViewRootImpl root = getViewRootImpl();
-        if (root != null) {
-            root.setDrawDuringWindowsAnimating(true);
-        }
-
         // We need to ensure that our window doesn't suffer from overdraw which would normally
         // occur if our window is translucent. Since we are drawing the whole window anyway with
         // the scrim, we don't need the window to be cleared in the beginning.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
index ed9b123..4f7756e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
@@ -102,6 +102,7 @@
     private boolean mHeadsUpGoingAway;
     private boolean mWaitingOnCollapseWhenGoingAway;
     private boolean mIsObserving;
+    private boolean mRemoteInputActive;
 
     public HeadsUpManager(final Context context, View statusBarWindowView) {
         mContext = context;
@@ -536,6 +537,18 @@
         return clicked != null && clicked;
     }
 
+    public void setRemoteInputActive(NotificationData.Entry entry, boolean remoteInputActive) {
+        HeadsUpEntry headsUpEntry = mHeadsUpEntries.get(entry.key);
+        if (headsUpEntry != null && headsUpEntry.remoteInputActive != remoteInputActive) {
+            headsUpEntry.remoteInputActive = remoteInputActive;
+            if (remoteInputActive) {
+                headsUpEntry.removeAutoRemovalCallbacks();
+            } else {
+                headsUpEntry.updateEntry(false /* updatePostTime */);
+            }
+        }
+    }
+
     /**
      * This represents a notification and how long it is in a heads up mode. It also manages its
      * lifecycle automatically when created.
@@ -545,6 +558,7 @@
         public long postTime;
         public long earliestRemovaltime;
         private Runnable mRemoveHeadsUpRunnable;
+        public boolean remoteInputActive;
 
         public void setEntry(final NotificationData.Entry entry) {
             this.entry = entry;
@@ -565,12 +579,18 @@
         }
 
         public void updateEntry() {
+            updateEntry(true);
+        }
+
+        public void updateEntry(boolean updatePostTime) {
             mSortedEntries.remove(HeadsUpEntry.this);
             long currentTime = mClock.currentTimeMillis();
             earliestRemovaltime = currentTime + mMinimumDisplayTime;
-            postTime = Math.max(postTime, currentTime);
+            if (updatePostTime) {
+                postTime = Math.max(postTime, currentTime);
+            }
             removeAutoRemovalCallbacks();
-            if (!hasFullScreenIntent(entry)) {
+            if (!hasFullScreenIntent(entry) && !mRemoteInputActive) {
                 long finishTime = postTime + mHeadsUpNotificationDecay;
                 long removeDelay = Math.max(finishTime - currentTime, mMinimumDisplayTime);
                 mHandler.postDelayed(mRemoveHeadsUpRunnable, removeDelay);
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 7d721c2..2ad9287 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -17,6 +17,8 @@
 package com.android.systemui.statusbar.policy;
 
 import com.android.systemui.R;
+import com.android.systemui.statusbar.NotificationData;
+import com.android.systemui.statusbar.RemoteInputController;
 
 import android.annotation.NonNull;
 import android.app.Notification;
@@ -33,12 +35,15 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputConnection;
 import android.view.inputmethod.InputMethodManager;
 import android.widget.EditText;
 import android.widget.FrameLayout;
 import android.widget.ProgressBar;
 import android.widget.TextView;
 
+import java.util.ArrayList;
+
 /**
  * Host for the remote input.
  */
@@ -51,6 +56,8 @@
     private PendingIntent mPendingIntent;
     private RemoteInput mRemoteInput;
     private Notification.Action mAction;
+    private RemoteInputController mController;
+    private NotificationData.Entry mEntry;
 
     public RemoteInputView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -85,12 +92,13 @@
         });
         mEditText.setOnClickListener(this);
         mEditText.setInnerFocusable(false);
+        mEditText.mDefocusListener = this;
     }
 
     private void sendRemoteInput() {
         Bundle results = new Bundle();
         results.putString(mRemoteInput.getResultKey(), mEditText.getText().toString());
-        Intent fillInIntent = new Intent();
+        Intent fillInIntent = new Intent().addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
         RemoteInput.addResultsToIntent(mAction.getRemoteInputs(), fillInIntent,
                 results);
 
@@ -105,7 +113,8 @@
     }
 
     public static RemoteInputView inflate(Context context, ViewGroup root,
-            Notification.Action action, RemoteInput remoteInput) {
+            NotificationData.Entry entry, Notification.Action action, RemoteInput remoteInput,
+            RemoteInputController controller) {
         RemoteInputView v = (RemoteInputView)
                 LayoutInflater.from(context).inflate(R.layout.remote_input, root, false);
 
@@ -113,6 +122,8 @@
         v.mPendingIntent = action.actionIntent;
         v.mRemoteInput = remoteInput;
         v.mAction = action;
+        v.mController = controller;
+        v.mEntry = entry;
 
         return v;
     }
@@ -122,15 +133,22 @@
         if (v == mEditText) {
             if (!mEditText.isFocusable()) {
                 mEditText.setInnerFocusable(true);
-                InputMethodManager imm = InputMethodManager.getInstance();
-                if (imm != null) {
-                    imm.viewClicked(mEditText);
-                    imm.showSoftInput(mEditText, 0);
-                }
+                mController.addRemoteInput(mEntry);
+                mEditText.mShowImeOnInputConnection = true;
             }
         }
     }
 
+    public void onDefocus() {
+        mController.removeRemoteInput(mEntry);
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        mController.removeRemoteInput(mEntry);
+    }
+
     /**
      * An EditText that changes appearance based on whether it's focusable and becomes
      * un-focusable whenever the user navigates away from it or it becomes invisible.
@@ -138,6 +156,8 @@
     public static class RemoteEditText extends EditText {
 
         private final Drawable mBackground;
+        private RemoteInputView mDefocusListener;
+        boolean mShowImeOnInputConnection;
 
         public RemoteEditText(Context context, AttributeSet attrs) {
             super(context, attrs);
@@ -147,6 +167,10 @@
         private void defocusIfNeeded() {
             if (isFocusable() && isEnabled()) {
                 setInnerFocusable(false);
+                if (mDefocusListener != null) {
+                    mDefocusListener.onDefocus();
+                }
+                mShowImeOnInputConnection = false;
             }
         }
 
@@ -173,6 +197,28 @@
             return super.onKeyPreIme(keyCode, event);
         }
 
+        @Override
+        public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
+            final InputConnection inputConnection = super.onCreateInputConnection(outAttrs);
+
+            if (mShowImeOnInputConnection && inputConnection != null) {
+                final InputMethodManager imm = InputMethodManager.getInstance();
+                if (imm != null) {
+                    // onCreateInputConnection is called by InputMethodManager in the middle of
+                    // setting up the connection to the IME; wait with requesting the IME until that
+                    // work has completed.
+                    post(new Runnable() {
+                        @Override
+                        public void run() {
+                            imm.viewClicked(RemoteEditText.this);
+                            imm.showSoftInput(RemoteEditText.this, 0);
+                        }
+                    });
+                }
+            }
+
+            return inputConnection;
+        }
 
         void setInnerFocusable(boolean focusable) {
             setFocusableInTouchMode(focusable);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java
index 40984d4..f06e5d3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java
@@ -22,6 +22,7 @@
     String getDeviceOwnerName();
     String getProfileOwnerName();
     boolean isVpnEnabled();
+    boolean isVpnRestricted();
     String getPrimaryVpnName();
     String getProfileVpnName();
     void onUserSwitched(int newUserId);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
index e0823b4..88f028f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
@@ -162,6 +162,13 @@
     }
 
     @Override
+    public boolean isVpnRestricted() {
+        UserHandle currentUser = new UserHandle(mCurrentUserId);
+        return mUserManager.getUserInfo(mCurrentUserId).isRestricted()
+                || mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_VPN, currentUser);
+    }
+
+    @Override
     public void removeCallback(SecurityControllerCallback callback) {
         synchronized (mCallbacks) {
             if (callback == null) return;
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/QsTuner.java b/packages/SystemUI/src/com/android/systemui/tuner/QsTuner.java
index 703ee661..3ac2a94 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/QsTuner.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/QsTuner.java
@@ -350,6 +350,11 @@
             }
 
             @Override
+            public boolean isVpnRestricted() {
+                return false;
+            }
+
+            @Override
             public String getPrimaryVpnName() {
                 return null;
             }
diff --git a/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java b/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java
index 48e0582..f0ca441 100644
--- a/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java
+++ b/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java
@@ -18,8 +18,11 @@
 
 import android.content.Context;
 import android.content.DialogInterface;
+import android.content.pm.PackageManager;
 import android.graphics.drawable.Drawable;
 import android.net.IConnectivityManager;
+import android.os.Bundle;
+import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.UserHandle;
 import android.text.Html;
@@ -40,43 +43,47 @@
 
     private IConnectivityManager mService;
 
-    private Button mButton;
-
     @Override
-    protected void onResume() {
-        super.onResume();
-        try {
-            mPackage = getCallingPackage();
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mPackage = getCallingPackage();
+        mService = IConnectivityManager.Stub.asInterface(
+                ServiceManager.getService(Context.CONNECTIVITY_SERVICE));
 
-            mService = IConnectivityManager.Stub.asInterface(
-                    ServiceManager.getService(Context.CONNECTIVITY_SERVICE));
-
-            if (mService.prepareVpn(mPackage, null, UserHandle.myUserId())) {
-                setResult(RESULT_OK);
-                finish();
-                return;
-            }
-
-            View view = View.inflate(this, R.layout.confirm, null);
-
-            ((TextView) view.findViewById(R.id.warning)).setText(
-                    Html.fromHtml(
-                            getString(R.string.warning, VpnConfig.getVpnLabel(this, mPackage)),
-                    this, null /* tagHandler */));
-
-            mAlertParams.mTitle = getText(R.string.prompt);
-            mAlertParams.mPositiveButtonText = getText(android.R.string.ok);
-            mAlertParams.mPositiveButtonListener = this;
-            mAlertParams.mNegativeButtonText = getText(android.R.string.cancel);
-            mAlertParams.mView = view;
-            setupAlert();
-
-            getWindow().setCloseOnTouchOutside(false);
-            mButton = mAlert.getButton(DialogInterface.BUTTON_POSITIVE);
-            mButton.setFilterTouchesWhenObscured(true);
-        } catch (Exception e) {
-            Log.e(TAG, "onResume", e);
+        if (prepareVpn()) {
+            setResult(RESULT_OK);
             finish();
+            return;
+        }
+        View view = View.inflate(this, R.layout.confirm, null);
+        ((TextView) view.findViewById(R.id.warning)).setText(
+                Html.fromHtml(getString(R.string.warning, getVpnLabel()),
+                        this, null /* tagHandler */));
+        mAlertParams.mTitle = getText(R.string.prompt);
+        mAlertParams.mPositiveButtonText = getText(android.R.string.ok);
+        mAlertParams.mPositiveButtonListener = this;
+        mAlertParams.mNegativeButtonText = getText(android.R.string.cancel);
+        mAlertParams.mView = view;
+        setupAlert();
+
+        getWindow().setCloseOnTouchOutside(false);
+        Button button = mAlert.getButton(DialogInterface.BUTTON_POSITIVE);
+        button.setFilterTouchesWhenObscured(true);
+    }
+
+    private boolean prepareVpn() {
+        try {
+            return mService.prepareVpn(mPackage, null, UserHandle.myUserId());
+        } catch (RemoteException e) {
+            throw new IllegalStateException(e);
+        }
+    }
+
+    private CharSequence getVpnLabel() {
+        try {
+            return VpnConfig.getVpnLabel(this, mPackage);
+        } catch (PackageManager.NameNotFoundException e) {
+            throw new IllegalStateException(e);
         }
     }
 
diff --git a/rs/java/android/renderscript/RenderScript.java b/rs/java/android/renderscript/RenderScript.java
index a2967e8..7eb8005 100644
--- a/rs/java/android/renderscript/RenderScript.java
+++ b/rs/java/android/renderscript/RenderScript.java
@@ -1583,9 +1583,21 @@
 
             nContextDeinitToClient(mContext);
             mMessageThread.mRun = false;
-            try {
-                mMessageThread.join();
-            } catch(InterruptedException e) {
+
+            // Wait for mMessageThread to join.  Try in a loop, in case this thread gets interrupted
+            // during the wait.  If interrupted, set the "interrupted" status of the current thread.
+            boolean hasJoined = false, interrupted = false;
+            while (!hasJoined) {
+                try {
+                    mMessageThread.join();
+                    hasJoined = true;
+                } catch (InterruptedException e) {
+                    interrupted = true;
+                }
+            }
+            if (interrupted) {
+                Log.v(LOG_TAG, "Interrupted during wait for MessageThread to join");
+                Thread.currentThread().interrupt();
             }
 
             nContextDestroy();
diff --git a/services/accessibility/java/com/android/server/accessibility/AutoclickController.java b/services/accessibility/java/com/android/server/accessibility/AutoclickController.java
index 3283378..892e9da 100644
--- a/services/accessibility/java/com/android/server/accessibility/AutoclickController.java
+++ b/services/accessibility/java/com/android/server/accessibility/AutoclickController.java
@@ -32,6 +32,7 @@
 import android.view.MotionEvent.PointerCoords;
 import android.view.MotionEvent.PointerProperties;
 import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
 
 /**
  * Implements "Automatically click on mouse stop" feature.
@@ -56,8 +57,6 @@
  */
 public class AutoclickController implements EventStreamTransformation {
 
-    public static final int DEFAULT_CLICK_DELAY_MS = 600;
-
     private static final String LOG_TAG = AutoclickController.class.getSimpleName();
 
     private EventStreamTransformation mNext;
@@ -78,7 +77,8 @@
         if (event.isFromSource(InputDevice.SOURCE_MOUSE)) {
             if (mClickScheduler == null) {
                 Handler handler = new Handler(mContext.getMainLooper());
-                mClickScheduler = new ClickScheduler(handler, DEFAULT_CLICK_DELAY_MS);
+                mClickScheduler =
+                        new ClickScheduler(handler, AccessibilityManager.AUTOCLICK_DELAY_DEFAULT);
                 mClickDelayObserver = new ClickDelayObserver(mUserId, handler);
                 mClickDelayObserver.start(mContext.getContentResolver(), mClickScheduler);
             }
@@ -230,7 +230,7 @@
             if (mAutoclickDelaySettingUri.equals(uri)) {
                 int delay = Settings.Secure.getIntForUser(
                         mContentResolver, Settings.Secure.ACCESSIBILITY_AUTOCLICK_DELAY,
-                        DEFAULT_CLICK_DELAY_MS, mUserId);
+                        AccessibilityManager.AUTOCLICK_DELAY_DEFAULT, mUserId);
                 mClickScheduler.updateDelay(delay);
             }
         }
diff --git a/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
index c3f5c43..f6b32f7 100644
--- a/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
+++ b/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
@@ -112,12 +112,6 @@
     // Timeout before trying to decide what the user is trying to do.
     private final int mDetermineUserIntentTimeout;
 
-    // Timeout within which we try to detect a tap.
-    private final int mTapTimeout;
-
-    // Slop between the down and up tap to be a tap.
-    private final int mTouchSlop;
-
     // Slop between the first and second tap to be a double tap.
     private final int mDoubleTapSlop;
 
@@ -142,9 +136,6 @@
     // Command for delayed sending of touch interaction end events.
     private final SendAccessibilityEventDelayed mSendTouchInteractionEndDelayed;
 
-    // Command for delayed sending of a long press.
-    private final PerformLongPressDelayed mPerformLongPressDelayed;
-
     // Command for exiting gesture detection mode after a timeout.
     private final ExitGestureDetectionModeDelayed mExitGestureDetectionModeDelayed;
 
@@ -173,9 +164,6 @@
     // Handle to the accessibility manager service.
     private final AccessibilityManagerService mAms;
 
-    // Temporary rectangle to avoid instantiation.
-    private final Rect mTempRect = new Rect();
-
     // Temporary point to avoid instantiation.
     private final Point mTempPoint = new Point();
 
@@ -226,12 +214,9 @@
         mAms = service;
         mReceivedPointerTracker = new ReceivedPointerTracker();
         mInjectedPointerTracker = new InjectedPointerTracker();
-        mTapTimeout = ViewConfiguration.getTapTimeout();
         mDetermineUserIntentTimeout = ViewConfiguration.getDoubleTapTimeout();
-        mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
         mDoubleTapSlop = ViewConfiguration.get(context).getScaledDoubleTapSlop();
         mHandler = new Handler(context.getMainLooper());
-        mPerformLongPressDelayed = new PerformLongPressDelayed();
         mExitGestureDetectionModeDelayed = new ExitGestureDetectionModeDelayed();
         mGestureLibrary = GestureLibraries.fromRawResource(context, R.raw.accessibility_gestures);
         mGestureLibrary.setOrientationStyle(8);
@@ -299,7 +284,6 @@
         // Remove all pending callbacks.
         mSendHoverEnterAndMoveDelayed.cancel();
         mSendHoverExitDelayed.cancel();
-        mPerformLongPressDelayed.cancel();
         mExitGestureDetectionModeDelayed.cancel();
         mSendTouchExplorationEndDelayed.cancel();
         mSendTouchInteractionEndDelayed.cancel();
@@ -437,7 +421,6 @@
                 // we resent the delayed callback and wait again.
                 mSendHoverEnterAndMoveDelayed.cancel();
                 mSendHoverExitDelayed.cancel();
-                mPerformLongPressDelayed.cancel();
 
                 if (mSendTouchExplorationEndDelayed.isPending()) {
                     mSendTouchExplorationEndDelayed.forceSendAndRemove();
@@ -447,18 +430,7 @@
                     mSendTouchInteractionEndDelayed.forceSendAndRemove();
                 }
 
-                // If we have the first tap, schedule a long press and break
-                // since we do not want to schedule hover enter because
-                // the delayed callback will kick in before the long click.
-                // This would lead to a state transition resulting in long
-                // pressing the item below the double taped area which is
-                // not necessary where accessibility focus is.
-                if (mDoubleTapDetector.firstTapDetected()) {
-                    // We got a tap now post a long press action.
-                    mPerformLongPressDelayed.post(event, policyFlags);
-                    break;
-                }
-                if (!mTouchExplorationInProgress) {
+                if (!mDoubleTapDetector.firstTapDetected() && !mTouchExplorationInProgress) {
                     if (!mSendHoverEnterAndMoveDelayed.isPending()) {
                         // Deliver hover enter with a delay to have a chance
                         // to detect what the user is trying to do.
@@ -478,7 +450,6 @@
                 // decide what we will actually do next.
                 mSendHoverEnterAndMoveDelayed.cancel();
                 mSendHoverExitDelayed.cancel();
-                mPerformLongPressDelayed.cancel();
             } break;
             case MotionEvent.ACTION_MOVE: {
                 final int pointerId = receivedTracker.getPrimaryPointerId();
@@ -521,7 +492,6 @@
                                     mVelocityTracker.clear();
                                     mSendHoverEnterAndMoveDelayed.cancel();
                                     mSendHoverExitDelayed.cancel();
-                                    mPerformLongPressDelayed.cancel();
                                     mExitGestureDetectionModeDelayed.post();
                                     // Send accessibility event to announce the start
                                     // of gesture recognition.
@@ -532,28 +502,12 @@
                                     // exploring so start sending events.
                                     mSendHoverEnterAndMoveDelayed.forceSendAndRemove();
                                     mSendHoverExitDelayed.cancel();
-                                    mPerformLongPressDelayed.cancel();
                                     sendMotionEvent(event, MotionEvent.ACTION_HOVER_MOVE,
                                             pointerIdBits, policyFlags);
                                 }
                                 break;
                             }
                         } else {
-                            // Cancel the long press if pending and the user
-                            // moved more than the slop.
-                            if (mPerformLongPressDelayed.isPending()) {
-                                final float deltaX =
-                                        receivedTracker.getReceivedPointerDownX(pointerId)
-                                        - rawEvent.getX(pointerIndex);
-                                final float deltaY =
-                                        receivedTracker.getReceivedPointerDownY(pointerId)
-                                        - rawEvent.getY(pointerIndex);
-                                final double moveDelta = Math.hypot(deltaX, deltaY);
-                                // The user has moved enough for us to decide.
-                                if (moveDelta > mTouchSlop) {
-                                    mPerformLongPressDelayed.cancel();
-                                }
-                            }
                             if (mTouchExplorationInProgress) {
                                 sendTouchExplorationGestureStartAndHoverEnterIfNeeded(policyFlags);
                                 sendMotionEvent(event, MotionEvent.ACTION_HOVER_MOVE, pointerIdBits,
@@ -569,9 +523,7 @@
                             // scheduled sending events.
                             mSendHoverEnterAndMoveDelayed.cancel();
                             mSendHoverExitDelayed.cancel();
-                            mPerformLongPressDelayed.cancel();
                         } else {
-                            mPerformLongPressDelayed.cancel();
                             if (mTouchExplorationInProgress) {
                                 // If the user is touch exploring the second pointer may be
                                 // performing a double tap to activate an item without need
@@ -620,9 +572,7 @@
                             // scheduled sending events.
                             mSendHoverEnterAndMoveDelayed.cancel();
                             mSendHoverExitDelayed.cancel();
-                            mPerformLongPressDelayed.cancel();
                         } else {
-                            mPerformLongPressDelayed.cancel();
                             // We are sending events so send exit and gesture
                             // end since we transition to another state.
                             sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags);
@@ -643,7 +593,6 @@
                 final int pointerId = event.getPointerId(event.getActionIndex());
                 final int pointerIdBits = (1 << pointerId);
 
-                mPerformLongPressDelayed.cancel();
                 mVelocityTracker.clear();
 
                 if (mSendHoverEnterAndMoveDelayed.isPending()) {
@@ -1110,6 +1059,7 @@
         private final GestureDetector mGestureDetector;
         private boolean mFirstTapDetected;
         private boolean mDoubleTapDetected;
+        private int mPolicyFlags;
 
         DoubleTapDetector(Context context) {
             mGestureDetector = new GestureDetector(context, this);
@@ -1117,6 +1067,7 @@
         }
 
         public void onMotionEvent(MotionEvent event, int policyFlags) {
+            mPolicyFlags = policyFlags;
             switch (event.getActionMasked()) {
                 case MotionEvent.ACTION_DOWN:
                     mDoubleTapDetected = false;
@@ -1135,6 +1086,11 @@
         }
 
         @Override
+        public void onLongPress(MotionEvent e) {
+            maybeSendLongPress(e, mPolicyFlags);
+        }
+
+        @Override
         public boolean onSingleTapUp(MotionEvent event) {
             mFirstTapDetected = true;
             return false;
@@ -1154,6 +1110,38 @@
             return true;
         }
 
+        private void maybeSendLongPress(MotionEvent event, int policyFlags) {
+            if (!mDoubleTapDetected) {
+                return;
+            }
+
+            clear();
+
+            // Pointers should not be zero when running this command.
+            if (mReceivedPointerTracker.getLastReceivedEvent().getPointerCount() == 0) {
+                return;
+            }
+
+            final int pointerIndex = event.getActionIndex();
+            final int pointerId = event.getPointerId(pointerIndex);
+
+            Point clickLocation = mTempPoint;
+            final int result = computeClickLocation(clickLocation);
+
+            if (result == CLICK_LOCATION_NONE) {
+                return;
+            }
+
+            mLongPressingPointerId = pointerId;
+            mLongPressingPointerDeltaX = (int) event.getX(pointerIndex) - clickLocation.x;
+            mLongPressingPointerDeltaY = (int) event.getY(pointerIndex) - clickLocation.y;
+
+            sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags);
+
+            mCurrentState = STATE_DELEGATING;
+            sendDownForAllNotInjectedPointers(event, policyFlags);
+        }
+
         private void maybeFinishDoubleTap(MotionEvent event, int policyFlags) {
             if (!mDoubleTapDetected) {
                 return;
@@ -1169,7 +1157,6 @@
             // Remove pending event deliveries.
             mSendHoverEnterAndMoveDelayed.cancel();
             mSendHoverExitDelayed.cancel();
-            mPerformLongPressDelayed.cancel();
 
             if (mSendTouchExplorationEndDelayed.isPending()) {
                 mSendTouchExplorationEndDelayed.forceSendAndRemove();
@@ -1178,8 +1165,8 @@
                 mSendTouchInteractionEndDelayed.forceSendAndRemove();
             }
 
-            final int pointerId = event.getPointerId(event.getActionIndex());
-            final int pointerIndex = event.findPointerIndex(pointerId);
+            final int pointerIndex = event.getActionIndex();
+            final int pointerId = event.getPointerId(pointerIndex);
 
             Point clickLocation = mTempPoint;
             final int result = computeClickLocation(clickLocation);
@@ -1306,65 +1293,6 @@
     }
 
     /**
-     * Class for delayed sending of long press.
-     */
-    private final class PerformLongPressDelayed implements Runnable {
-        private MotionEvent mEvent;
-        private int mPolicyFlags;
-
-        public void post(MotionEvent prototype, int policyFlags) {
-            mEvent = MotionEvent.obtain(prototype);
-            mPolicyFlags = policyFlags;
-            mHandler.postDelayed(this, ViewConfiguration.getLongPressTimeout());
-        }
-
-        public void cancel() {
-            if (mEvent != null) {
-                mHandler.removeCallbacks(this);
-                clear();
-            }
-        }
-
-        private boolean isPending() {
-            return mHandler.hasCallbacks(this);
-        }
-
-        @Override
-        public void run() {
-            // Pointers should not be zero when running this command.
-            if (mReceivedPointerTracker.getLastReceivedEvent().getPointerCount() == 0) {
-                return;
-            }
-
-            final int pointerId = mEvent.getPointerId(mEvent.getActionIndex());
-            final int pointerIndex = mEvent.findPointerIndex(pointerId);
-
-            Point clickLocation = mTempPoint;
-            final int result = computeClickLocation(clickLocation);
-
-            if (result == CLICK_LOCATION_NONE) {
-                return;
-            }
-
-            mLongPressingPointerId = pointerId;
-            mLongPressingPointerDeltaX = (int) mEvent.getX(pointerIndex) - clickLocation.x;
-            mLongPressingPointerDeltaY = (int) mEvent.getY(pointerIndex) - clickLocation.y;
-
-            sendHoverExitAndTouchExplorationGestureEndIfNeeded(mPolicyFlags);
-
-            mCurrentState = STATE_DELEGATING;
-            sendDownForAllNotInjectedPointers(mEvent, mPolicyFlags);
-            clear();
-        }
-
-        private void clear() {
-            mEvent.recycle();
-            mEvent = null;
-            mPolicyFlags = 0;
-        }
-    }
-
-    /**
      * Class for delayed sending of hover enter and move events.
      */
     class SendHoverEnterAndMoveDelayed implements Runnable {
diff --git a/services/core/java/com/android/server/GestureLauncherService.java b/services/core/java/com/android/server/GestureLauncherService.java
index f245985..2aa0390 100644
--- a/services/core/java/com/android/server/GestureLauncherService.java
+++ b/services/core/java/com/android/server/GestureLauncherService.java
@@ -55,7 +55,8 @@
      * Time in milliseconds in which the power button must be pressed twice so it will be considered
      * as a camera launch.
      */
-    private static final long CAMERA_POWER_DOUBLE_TAP_TIME_MS = 300;
+    private static final long CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS = 300;
+    private static final long CAMERA_POWER_DOUBLE_TAP_MIN_TIME_MS = 120;
 
     /** The listener that receives the gesture event. */
     private final GestureEventListener mGestureListener = new GestureEventListener();
@@ -256,14 +257,16 @@
         synchronized (this) {
             doubleTapInterval = event.getEventTime() - mLastPowerDown;
             if (mCameraDoubleTapPowerEnabled
-                    && doubleTapInterval < CAMERA_POWER_DOUBLE_TAP_TIME_MS) {
+                    && doubleTapInterval < CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS
+                    && doubleTapInterval > CAMERA_POWER_DOUBLE_TAP_MIN_TIME_MS) {
                 launched = true;
                 intercept = interactive;
             }
             mLastPowerDown = event.getEventTime();
         }
         if (launched) {
-            Slog.i(TAG, "Power button double tap gesture detected, launching camera.");
+            Slog.i(TAG, "Power button double tap gesture detected, launching camera. Interval="
+                    + doubleTapInterval + "ms");
             launched = handleCameraLaunchGesture(false /* useWakelock */,
                     StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP);
             if (launched) {
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index 9dad7a1..ab1d775 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -37,6 +37,7 @@
 import org.xmlpull.v1.XmlSerializer;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.ActivityManagerNative;
 import android.app.AlertDialog;
 import android.app.AppGlobals;
@@ -280,8 +281,19 @@
     boolean mSystemReady;
 
     /**
-     * Id of the currently selected input method.
+     * Id obtained with {@link InputMethodInfo#getId()} for the currently selected input method.
+     * method.  This is to be synchronized with the secure settings keyed with
+     * {@link Settings.Secure#DEFAULT_INPUT_METHOD}.
+     *
+     * <p>This can be transiently {@code null} when the system is re-initializing input method
+     * settings, e.g., the system locale is just changed.</p>
+     *
+     * <p>Note that {@link #mCurId} is used to track which IME is being connected to
+     * {@link InputMethodManagerService}.</p>
+     *
+     * @see #mCurId
      */
+    @Nullable
     String mCurMethodId;
 
     /**
@@ -311,9 +323,14 @@
     EditorInfo mCurAttribute;
 
     /**
-     * The input method ID of the input method service that we are currently
+     * Id obtained with {@link InputMethodInfo#getId()} for the input method that we are currently
      * connected to or in the process of connecting to.
+     *
+     * <p>This can be {@code null} when no input method is connected.</p>
+     *
+     * @see #mCurMethodId
      */
+    @Nullable
     String mCurId;
 
     /**
@@ -918,7 +935,6 @@
                 || (newLocale != null && !newLocale.equals(mLastSystemLocale))) {
             if (!updateOnlyWhenLocaleChanged) {
                 hideCurrentInputLocked(0, null);
-                mCurMethodId = null;
                 unbindCurrentMethodLocked(true, false);
             }
             if (DEBUG) {
@@ -1474,7 +1490,11 @@
         channel.dispose();
     }
 
-    void unbindCurrentMethodLocked(boolean reportToClient, boolean savePosition) {
+    void unbindCurrentMethodLocked(boolean resetCurrentMethodAndClient, boolean savePosition) {
+        if (resetCurrentMethodAndClient) {
+            mCurMethodId = null;
+        }
+
         if (mVisibleBound) {
             mContext.unbindService(mVisibleConnection);
             mVisibleBound = false;
@@ -1501,9 +1521,8 @@
         mCurId = null;
         clearCurMethodLocked();
 
-        if (reportToClient && mCurClient != null) {
-            executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIO(
-                    MSG_UNBIND_METHOD, mCurSeq, mCurClient.client));
+        if (resetCurrentMethodAndClient) {
+            unbindCurrentClientLocked();
         }
     }
 
@@ -1857,13 +1876,11 @@
                 setInputMethodLocked(id, mSettings.getSelectedInputMethodSubtypeId(id));
             } catch (IllegalArgumentException e) {
                 Slog.w(TAG, "Unknown input method from prefs: " + id, e);
-                mCurMethodId = null;
                 unbindCurrentMethodLocked(true, false);
             }
             mShortcutInputMethodsAndSubtypes.clear();
         } else {
             // There is no longer an input method set, so stop any current one.
-            mCurMethodId = null;
             unbindCurrentMethodLocked(true, false);
         }
         // Here is not the perfect place to reset the switching controller. Ideally
diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java
index c3d32c2..4d32599 100644
--- a/services/core/java/com/android/server/MountService.java
+++ b/services/core/java/com/android/server/MountService.java
@@ -29,6 +29,7 @@
 
 import android.Manifest;
 import android.annotation.Nullable;
+import android.app.ActivityManager;
 import android.app.ActivityManagerNative;
 import android.app.AppOpsManager;
 import android.app.IActivityManager;
@@ -90,7 +91,6 @@
 import libcore.util.EmptyArray;
 
 import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.IMediaContainerService;
 import com.android.internal.os.SomeArgs;
 import com.android.internal.os.Zygote;
@@ -290,7 +290,7 @@
     private ArrayMap<String, DiskInfo> mDisks = new ArrayMap<>();
     /** Map from volume ID to disk */
     @GuardedBy("mLock")
-    private ArrayMap<String, VolumeInfo> mVolumes = new ArrayMap<>();
+    private final ArrayMap<String, VolumeInfo> mVolumes = new ArrayMap<>();
 
     /** Map from UUID to record */
     @GuardedBy("mLock")
@@ -462,11 +462,7 @@
         public ObbState(String rawPath, String canonicalPath, int callingUid,
                 IObbActionListener token, int nonce) {
             this.rawPath = rawPath;
-            this.canonicalPath = canonicalPath.toString();
-
-            final int userId = UserHandle.getUserId(callingUid);
-            this.ownerPath = buildObbPath(canonicalPath, userId, false);
-            this.voldPath = buildObbPath(canonicalPath, userId, true);
+            this.canonicalPath = canonicalPath;
 
             this.ownerGid = UserHandle.getSharedAppGid(callingUid);
             this.token = token;
@@ -475,8 +471,6 @@
 
         final String rawPath;
         final String canonicalPath;
-        final String ownerPath;
-        final String voldPath;
 
         final int ownerGid;
 
@@ -509,8 +503,6 @@
             StringBuilder sb = new StringBuilder("ObbState{");
             sb.append("rawPath=").append(rawPath);
             sb.append(",canonicalPath=").append(canonicalPath);
-            sb.append(",ownerPath=").append(ownerPath);
-            sb.append(",voldPath=").append(voldPath);
             sb.append(",ownerGid=").append(ownerGid);
             sb.append(",token=").append(token);
             sb.append(",binder=").append(getBinder());
@@ -569,6 +561,7 @@
     private static final int H_VOLUME_MOUNT = 5;
     private static final int H_VOLUME_BROADCAST = 6;
     private static final int H_INTERNAL_BROADCAST = 7;
+    private static final int H_VOLUME_UNMOUNT = 8;
 
     class MountServiceHandler extends Handler {
         public MountServiceHandler(Looper looper) {
@@ -649,6 +642,11 @@
                     }
                     break;
                 }
+                case H_VOLUME_UNMOUNT: {
+                    final VolumeInfo vol = (VolumeInfo) msg.obj;
+                    unmount(vol.getId());
+                    break;
+                }
                 case H_VOLUME_BROADCAST: {
                     final StorageVolume userVol = (StorageVolume) msg.obj;
                     final String envState = userVol.getState();
@@ -683,6 +681,7 @@
         public void onReceive(Context context, Intent intent) {
             final String action = intent.getAction();
             final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
+            Preconditions.checkArgument(userId >= 0);
 
             try {
                 if (Intent.ACTION_USER_ADDED.equals(action)) {
@@ -690,6 +689,16 @@
                     final int userSerialNumber = um.getUserSerialNumber(userId);
                     mConnector.execute("volume", "user_added", userId, userSerialNumber);
                 } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
+                    synchronized (mVolumes) {
+                        final int size = mVolumes.size();
+                        for (int i = 0; i < size; i++) {
+                            final VolumeInfo vol = mVolumes.valueAt(i);
+                            if (vol.mountUserId == userId) {
+                                vol.mountUserId = UserHandle.USER_NULL;
+                                mHandler.obtainMessage(H_VOLUME_UNMOUNT, vol).sendToTarget();
+                            }
+                        }
+                    }
                     mConnector.execute("volume", "user_removed", userId);
                 }
             } catch (NativeDaemonConnectorException e) {
@@ -757,17 +766,26 @@
      * paths never changing, so we outright kill them to pick up new state.
      */
     @Deprecated
-    private void killMediaProvider() {
+    private void killMediaProvider(List<UserInfo> users) {
+        if (users == null) return;
+
         final long token = Binder.clearCallingIdentity();
         try {
-            final ProviderInfo provider = mPms.resolveContentProvider(MediaStore.AUTHORITY, 0,
-                    UserHandle.USER_OWNER);
-            if (provider != null) {
-                final IActivityManager am = ActivityManagerNative.getDefault();
-                try {
-                    am.killApplicationWithAppId(provider.applicationInfo.packageName,
-                            UserHandle.getAppId(provider.applicationInfo.uid), "vold reset");
-                } catch (RemoteException e) {
+            for (UserInfo user : users) {
+                // System user does not have media provider, so skip.
+                if (user.isSystemOnly()) continue;
+
+                final ProviderInfo provider =
+                        mPms.resolveContentProvider(MediaStore.AUTHORITY, 0, user.id);
+                if (provider != null) {
+                    final IActivityManager am = ActivityManagerNative.getDefault();
+                    try {
+                        am.killApplicationWithAppId(provider.applicationInfo.packageName,
+                                UserHandle.getAppId(provider.applicationInfo.uid), "vold reset");
+                        // We only need to run this once. It will kill all users' media processes.
+                        break;
+                    } catch (RemoteException e) {
+                    }
                 }
             }
         } finally {
@@ -788,7 +806,9 @@
         Slog.d(TAG, "Thinking about reset, mSystemReady=" + mSystemReady
                 + ", mDaemonConnected=" + mDaemonConnected);
         if (mSystemReady && mDaemonConnected) {
-            killMediaProvider();
+            final UserManager um = UserManager.get(mContext);
+            final List<UserInfo> users = um.getUsers();
+            killMediaProvider(users);
 
             mDisks.clear();
             mVolumes.clear();
@@ -799,8 +819,6 @@
                 mConnector.execute("volume", "reset");
 
                 // Tell vold about all existing and started users
-                final UserManager um = mContext.getSystemService(UserManager.class);
-                final List<UserInfo> users = um.getUsers();
                 for (UserInfo user : users) {
                     mConnector.execute("volume", "user_added", user.id, user.serialNumber);
                 }
@@ -1192,7 +1210,7 @@
                 vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE;
             }
 
-            vol.mountUserId = UserHandle.USER_OWNER;
+            vol.mountUserId = ActivityManager.getCurrentUser();
             mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget();
 
         } else if (vol.type == VolumeInfo.TYPE_PRIVATE) {
@@ -2285,7 +2303,7 @@
 
         final NativeDaemonEvent event;
         try {
-            event = mConnector.execute("obb", "path", state.voldPath);
+            event = mConnector.execute("obb", "path", state.canonicalPath);
             event.checkCode(VoldResponseCode.AsecPathResult);
             return event.getMessage();
         } catch (NativeDaemonConnectorException e) {
@@ -3038,14 +3056,14 @@
         protected ObbInfo getObbInfo() throws IOException {
             ObbInfo obbInfo;
             try {
-                obbInfo = mContainerService.getObbInfo(mObbState.ownerPath);
+                obbInfo = mContainerService.getObbInfo(mObbState.canonicalPath);
             } catch (RemoteException e) {
                 Slog.d(TAG, "Couldn't call DefaultContainerService to fetch OBB info for "
-                        + mObbState.ownerPath);
+                        + mObbState.canonicalPath);
                 obbInfo = null;
             }
             if (obbInfo == null) {
-                throw new IOException("Couldn't read OBB file: " + mObbState.ownerPath);
+                throw new IOException("Couldn't read OBB file: " + mObbState.canonicalPath);
             }
             return obbInfo;
         }
@@ -3122,7 +3140,7 @@
 
             int rc = StorageResultCode.OperationSucceeded;
             try {
-                mConnector.execute("obb", "mount", mObbState.voldPath, new SensitiveArg(hashedKey),
+                mConnector.execute("obb", "mount", mObbState.canonicalPath, new SensitiveArg(hashedKey),
                         mObbState.ownerGid);
             } catch (NativeDaemonConnectorException e) {
                 int code = e.getCode();
@@ -3133,7 +3151,7 @@
 
             if (rc == StorageResultCode.OperationSucceeded) {
                 if (DEBUG_OBB)
-                    Slog.d(TAG, "Successfully mounted OBB " + mObbState.voldPath);
+                    Slog.d(TAG, "Successfully mounted OBB " + mObbState.canonicalPath);
 
                 synchronized (mObbMounts) {
                     addObbStateLocked(mObbState);
@@ -3194,7 +3212,7 @@
 
             int rc = StorageResultCode.OperationSucceeded;
             try {
-                final Command cmd = new Command("obb", "unmount", mObbState.voldPath);
+                final Command cmd = new Command("obb", "unmount", mObbState.canonicalPath);
                 if (mForceUnmount) {
                     cmd.appendArg("force");
                 }
@@ -3240,49 +3258,6 @@
         }
     }
 
-    @VisibleForTesting
-    public static String buildObbPath(final String canonicalPath, int userId, boolean forVold) {
-        // TODO: allow caller to provide Environment for full testing
-        // TODO: extend to support OBB mounts on secondary external storage
-
-        // Only adjust paths when storage is emulated
-        if (!Environment.isExternalStorageEmulated()) {
-            return canonicalPath;
-        }
-
-        String path = canonicalPath.toString();
-
-        // First trim off any external storage prefix
-        final UserEnvironment userEnv = new UserEnvironment(userId);
-
-        // /storage/emulated/0
-        final String externalPath = userEnv.getExternalStorageDirectory().getAbsolutePath();
-        // /storage/emulated_legacy
-        final String legacyExternalPath = Environment.getLegacyExternalStorageDirectory()
-                .getAbsolutePath();
-
-        if (path.startsWith(externalPath)) {
-            path = path.substring(externalPath.length() + 1);
-        } else if (path.startsWith(legacyExternalPath)) {
-            path = path.substring(legacyExternalPath.length() + 1);
-        } else {
-            return canonicalPath;
-        }
-
-        // Handle special OBB paths on emulated storage
-        final String obbPath = "Android/obb";
-        if (path.startsWith(obbPath)) {
-            path = path.substring(obbPath.length() + 1);
-
-            final UserEnvironment ownerEnv = new UserEnvironment(UserHandle.USER_OWNER);
-            return new File(ownerEnv.buildExternalStorageAndroidObbDirs()[0], path)
-                    .getAbsolutePath();
-        }
-
-        // Handle normal external storage paths
-        return new File(userEnv.getExternalStorageDirectory(), path).getAbsolutePath();
-    }
-
     private static class Callbacks extends Handler {
         private static final int MSG_STORAGE_STATE_CHANGED = 1;
         private static final int MSG_VOLUME_STATE_CHANGED = 2;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index bd10c63..759a4f3 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -20,8 +20,11 @@
 import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
 import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
 import static android.app.ActivityManager.DOCKED_STACK_ID;
+import static android.app.ActivityManager.FREEFORM_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.FULLSCREEN_WORKSPACE_STACK_ID;
 import static android.app.ActivityManager.HOME_STACK_ID;
 import static android.app.ActivityManager.INVALID_STACK_ID;
+import static android.app.ActivityManager.RESIZE_MODE_PRESERVE_WINDOW;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static com.android.internal.util.XmlUtils.readBooleanAttribute;
 import static com.android.internal.util.XmlUtils.readIntAttribute;
@@ -33,6 +36,7 @@
 import static com.android.server.am.ActivityManagerDebugConfig.*;
 import static com.android.server.am.ActivityStackSupervisor.FORCE_FOCUS;
 import static com.android.server.am.ActivityStackSupervisor.ON_TOP;
+import static com.android.server.am.ActivityStackSupervisor.RESTORE_FROM_RECENTS;
 import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
 import static com.android.server.am.TaskRecord.INVALID_TASK_ID;
 import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_DONT_LOCK;
@@ -415,6 +419,9 @@
     // Lower delay than APP_BOOST_MESSAGE_DELAY to disable the boost
     static final int APP_BOOST_TIMEOUT = 2500;
 
+    // Used to indicate that a task is removed it should also be removed from recents.
+    private static final boolean REMOVE_FROM_RECENTS = true;
+
     private static native int nativeMigrateToBoost();
     private static native int nativeMigrateFromBoost();
     private boolean mIsBoosted = false;
@@ -2732,7 +2739,7 @@
         synchronized (ActivityManagerService.this) {
             ActivityStack stack = mStackSupervisor.getStack(stackId);
             if (stack != null) {
-                ActivityRecord r = stack.topRunningActivityLocked(null);
+                ActivityRecord r = stack.topRunningActivityLocked();
                 if (r != null) {
                     setFocusedActivityLocked(r, "setFocusedStack");
                     mStackSupervisor.resumeTopActivitiesLocked(stack, null, null);
@@ -2749,7 +2756,7 @@
             synchronized (ActivityManagerService.this) {
                 TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
                 if (task != null) {
-                    ActivityRecord r = task.topRunningActivityLocked(null);
+                    ActivityRecord r = task.topRunningActivityLocked();
                     if (r != null) {
                         setFocusedActivityLocked(r, "setFocusedTask");
                         mStackSupervisor.resumeTopActivitiesLocked(task.stack, null, null);
@@ -4185,27 +4192,39 @@
     }
 
     @Override
-    public final int startActivityFromRecents(int taskId, Bundle options) {
+    public final int startActivityFromRecents(int taskId, int launchStackId, Bundle options) {
         if (checkCallingPermission(START_TASKS_FROM_RECENTS) != PackageManager.PERMISSION_GRANTED) {
             String msg = "Permission Denial: startActivityFromRecents called without " +
                     START_TASKS_FROM_RECENTS;
             Slog.w(TAG, msg);
             throw new SecurityException(msg);
         }
-        return startActivityFromRecentsInner(taskId, options);
+        return startActivityFromRecentsInner(taskId, launchStackId, options);
     }
 
-    final int startActivityFromRecentsInner(int taskId, Bundle options) {
+    final int startActivityFromRecentsInner(int taskId, int launchStackId, Bundle options) {
         final TaskRecord task;
         final int callingUid;
         final String callingPackage;
         final Intent intent;
         final int userId;
         synchronized (this) {
-            task = mStackSupervisor.anyTaskForIdLocked(taskId);
-            if (task == null) {
-                throw new IllegalArgumentException("Task " + taskId + " not found.");
+            if (launchStackId == HOME_STACK_ID) {
+                throw new IllegalArgumentException("startActivityFromRecentsInner: Task "
+                        + taskId + " can't be launch in the home stack.");
             }
+
+            task = mStackSupervisor.anyTaskForIdLocked(taskId, RESTORE_FROM_RECENTS, launchStackId);
+            if (task == null) {
+                throw new IllegalArgumentException(
+                        "startActivityFromRecentsInner: Task " + taskId + " not found.");
+            }
+
+            if (launchStackId != INVALID_STACK_ID && task.stack.mStackId != launchStackId) {
+                mStackSupervisor.moveTaskToStackUncheckedLocked(
+                        task, launchStackId, ON_TOP, FORCE_FOCUS, "startActivityFromRecents");
+            }
+
             if (task.getRootActivity() != null) {
                 moveTaskToFrontLocked(task.taskId, 0, options);
                 return ActivityManager.START_TASK_TO_FRONT;
@@ -4366,12 +4385,16 @@
             final long origId = Binder.clearCallingIdentity();
             try {
                 boolean res;
+                final boolean finishWithRootActivity =
+                        finishTask == Activity.FINISH_TASK_WITH_ROOT_ACTIVITY;
                 if (finishTask == Activity.FINISH_TASK_WITH_ACTIVITY
-                        || (finishTask == Activity.FINISH_TASK_WITH_ROOT_ACTIVITY && r == rootR)) {
+                        || (finishWithRootActivity && r == rootR)) {
                     // If requested, remove the task that is associated to this activity only if it
                     // was the root activity in the task. The result code and data is ignored
-                    // because we don't support returning them across task boundaries.
-                    res = removeTaskByIdLocked(tr.taskId, false);
+                    // because we don't support returning them across task boundaries. Also, to
+                    // keep backwards compatibility we remove the task from recents when finishing
+                    // task with root activity.
+                    res = removeTaskByIdLocked(tr.taskId, false, finishWithRootActivity);
                     if (!res) {
                         Slog.i(TAG, "Removing task failed to finish activity");
                     }
@@ -5190,7 +5213,7 @@
                             tr.getBaseIntent().getComponent().getPackageName();
                     if (tr.userId != userId) continue;
                     if (!taskPackageName.equals(packageName)) continue;
-                    removeTaskByIdLocked(tr.taskId, false);
+                    removeTaskByIdLocked(tr.taskId, false, REMOVE_FROM_RECENTS);
                 }
             }
 
@@ -8547,7 +8570,8 @@
         synchronized (this) {
             enforceCallingPermission(android.Manifest.permission.READ_FRAME_BUFFER,
                     "getTaskThumbnail()");
-            TaskRecord tr = mStackSupervisor.anyTaskForIdLocked(id, false);
+            final TaskRecord tr = mStackSupervisor.anyTaskForIdLocked(
+                    id, !RESTORE_FROM_RECENTS, INVALID_STACK_ID);
             if (tr != null) {
                 return tr.getTaskThumbnailLocked();
             }
@@ -8660,7 +8684,8 @@
     @Override
     public void setTaskResizeable(int taskId, boolean resizeable) {
         synchronized (this) {
-            TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId, false);
+            final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(
+                    taskId, !RESTORE_FROM_RECENTS, INVALID_STACK_ID);
             if (task == null) {
                 Slog.w(TAG, "setTaskResizeable: taskId=" + taskId + " not found");
                 return;
@@ -8685,7 +8710,32 @@
                     Slog.w(TAG, "resizeTask: taskId=" + taskId + " not found");
                     return;
                 }
-                mStackSupervisor.resizeTaskLocked(task, bounds, resizeMode);
+                // Place the task in the right stack if it isn't there already based on
+                // the requested bounds.
+                // The stack transition logic is:
+                // - a null bounds on a freeform task moves that task to fullscreen
+                // - a non-null bounds on a non-freeform (fullscreen OR docked) task moves
+                //   that task to freeform
+                // - otherwise the task is not moved
+                // Note it's not allowed to resize a home stack task, or a docked task.
+                int stackId = task.stack.mStackId;
+                if (stackId == HOME_STACK_ID || stackId == DOCKED_STACK_ID) {
+                    throw new IllegalArgumentException("trying to resizeTask on a "
+                            + "home or docked task");
+                }
+                if (bounds == null && stackId == FREEFORM_WORKSPACE_STACK_ID) {
+                    stackId = FULLSCREEN_WORKSPACE_STACK_ID;
+                } else if (bounds != null && stackId != FREEFORM_WORKSPACE_STACK_ID ) {
+                    stackId = FREEFORM_WORKSPACE_STACK_ID;
+                }
+                boolean preserveWindow = (resizeMode & RESIZE_MODE_PRESERVE_WINDOW) != 0;
+                if (stackId != task.stack.mStackId) {
+                    mStackSupervisor.moveTaskToStackUncheckedLocked(
+                            task, stackId, ON_TOP, !FORCE_FOCUS, "resizeTask");
+                    preserveWindow = false;
+                }
+
+                mStackSupervisor.resizeTaskLocked(task, bounds, resizeMode, preserveWindow);
             }
         } finally {
             Binder.restoreCallingIdentity(ident);
@@ -8736,9 +8786,12 @@
         mWindowManager.executeAppTransition();
     }
 
-    private void cleanUpRemovedTaskLocked(TaskRecord tr, boolean killProcess) {
-        mRecentTasks.remove(tr);
-        tr.removedFromRecents();
+    private void cleanUpRemovedTaskLocked(TaskRecord tr, boolean killProcess,
+            boolean removeFromRecents) {
+        if (removeFromRecents) {
+            mRecentTasks.remove(tr);
+            tr.removedFromRecents();
+        }
         ComponentName component = tr.getBaseIntent().getComponent();
         if (component == null) {
             Slog.w(TAG, "No component for base intent of task: " + tr);
@@ -8815,7 +8868,7 @@
             ComponentName cn = tr.intent.getComponent();
             if (cn != null && cn.getPackageName().equals(packageName)) {
                 // If the package name matches, remove the task.
-                removeTaskByIdLocked(tr.taskId, true);
+                removeTaskByIdLocked(tr.taskId, true, REMOVE_FROM_RECENTS);
             }
         }
     }
@@ -8833,7 +8886,7 @@
             final boolean sameComponent = cn != null && cn.getPackageName().equals(packageName)
                     && (filterByClasses == null || filterByClasses.contains(cn.getClassName()));
             if (sameComponent) {
-                removeTaskByIdLocked(tr.taskId, false);
+                removeTaskByIdLocked(tr.taskId, false, REMOVE_FROM_RECENTS);
             }
         }
     }
@@ -8843,13 +8896,16 @@
      *
      * @param taskId Identifier of the task to be removed.
      * @param killProcess Kill any process associated with the task if possible.
+     * @param removeFromRecents Whether to also remove the task from recents.
      * @return Returns true if the given task was found and removed.
      */
-    private boolean removeTaskByIdLocked(int taskId, boolean killProcess) {
-        TaskRecord tr = mStackSupervisor.anyTaskForIdLocked(taskId, false);
+    private boolean removeTaskByIdLocked(int taskId, boolean killProcess,
+            boolean removeFromRecents) {
+        final TaskRecord tr = mStackSupervisor.anyTaskForIdLocked(
+                taskId, !RESTORE_FROM_RECENTS, INVALID_STACK_ID);
         if (tr != null) {
             tr.removeTaskActivitiesLocked();
-            cleanUpRemovedTaskLocked(tr, killProcess);
+            cleanUpRemovedTaskLocked(tr, killProcess, removeFromRecents);
             if (tr.isPersistable) {
                 notifyTaskPersisterLocked(null, true);
             }
@@ -8866,7 +8922,7 @@
                     "removeTask()");
             long ident = Binder.clearCallingIdentity();
             try {
-                return removeTaskByIdLocked(taskId, true);
+                return removeTaskByIdLocked(taskId, true, REMOVE_FROM_RECENTS);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -9175,7 +9231,8 @@
         long ident = Binder.clearCallingIdentity();
         try {
             synchronized (this) {
-                TaskRecord tr = mStackSupervisor.anyTaskForIdLocked(taskId, false);
+                final TaskRecord tr = mStackSupervisor.anyTaskForIdLocked(
+                        taskId, !RESTORE_FROM_RECENTS, INVALID_STACK_ID);
                 return tr != null && tr.stack != null && tr.stack.isHomeStack();
             }
         } finally {
@@ -11308,7 +11365,7 @@
     public boolean isTopActivityImmersive() {
         enforceNotIsolatedCaller("startActivity");
         synchronized (this) {
-            ActivityRecord r = getFocusedStack().topRunningActivityLocked(null);
+            ActivityRecord r = getFocusedStack().topRunningActivityLocked();
             return (r != null) ? r.immersive : false;
         }
     }
@@ -11893,7 +11950,8 @@
             updateCurrentProfileIdsLocked();
 
             mRecentTasks.clear();
-            mRecentTasks.addAll(mTaskPersister.restoreTasksLocked());
+            mRecentTasks.addAll(mTaskPersister.restoreTasksLocked(
+                    getUserManagerLocked().getUserIds()));
             mRecentTasks.cleanupLocked(UserHandle.USER_ALL);
             mTaskPersister.startPersisting();
 
@@ -17526,6 +17584,25 @@
     }
 
     @Override
+    public void removeStack(int stackId) {
+        enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS,
+                "detahStack()");
+        if (stackId == HOME_STACK_ID) {
+            throw new IllegalArgumentException("Removing home stack is not allowed.");
+        }
+        synchronized (this) {
+            ActivityStack stack = mStackSupervisor.getStack(stackId);
+            if (stack != null) {
+                ArrayList<TaskRecord> tasks = stack.getAllTasks();
+                for (int i = tasks.size() - 1; i >= 0; i--) {
+                    removeTaskByIdLocked(tasks.get(i).taskId, true /* killProcess */,
+                            !REMOVE_FROM_RECENTS);
+                }
+            }
+        }
+    }
+
+    @Override
     public void updatePersistentConfiguration(Configuration values) {
         enforceCallingPermission(android.Manifest.permission.CHANGE_CONFIGURATION,
                 "updateConfiguration()");
@@ -17698,7 +17775,7 @@
                 // If the configuration changed, and the caller is not already
                 // in the process of starting an activity, then find the top
                 // activity to check if its configuration needs to change.
-                starting = mainStack.topRunningActivityLocked(null);
+                starting = mainStack.topRunningActivityLocked();
             }
 
             if (starting != null) {
@@ -20644,9 +20721,6 @@
                 // Kill all the processes for the user.
                 forceStopUserLocked(userId, "finish user");
             }
-
-            // Explicitly remove the old information in mRecentTasks.
-            mRecentTasks.removeTasksForUserLocked(userId);
         }
 
         for (int i=0; i<callbacks.size(); i++) {
@@ -20665,6 +20739,10 @@
         }
     }
 
+    void onUserRemovedLocked(int userId) {
+        mRecentTasks.removeTasksForUserLocked(userId);
+    }
+
     @Override
     public UserInfo getCurrentUser() {
         if ((checkCallingPermission(INTERACT_ACROSS_USERS)
@@ -20949,6 +21027,13 @@
                 return homeActivity == null ? null : homeActivity.realActivity;
             }
         }
+
+        @Override
+        public void onUserRemoved(int userId) {
+            synchronized (ActivityManagerService.this) {
+                ActivityManagerService.this.onUserRemovedLocked(userId);
+            }
+        }
     }
 
     private final class SleepTokenImpl extends SleepToken {
@@ -21003,7 +21088,8 @@
             synchronized (ActivityManagerService.this) {
                 long origId = Binder.clearCallingIdentity();
                 try {
-                    if (!removeTaskByIdLocked(mTaskId, false)) {
+                    // We remove the task from recents to preserve backwards
+                    if (!removeTaskByIdLocked(mTaskId, false, REMOVE_FROM_RECENTS)) {
                         throw new IllegalArgumentException("Unable to find task ID " + mTaskId);
                     }
                 } finally {
@@ -21034,7 +21120,7 @@
         public void moveToFront() {
             checkCaller();
             // Will bring task to front if it already has a root activity.
-            startActivityFromRecentsInner(mTaskId, null);
+            startActivityFromRecentsInner(mTaskId, INVALID_STACK_ID, null);
         }
 
         @Override
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 24b90d8..4671cb0 100755
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -477,8 +477,8 @@
         }
     }
 
-    boolean isNotResolverActivity() {
-        return !ResolverActivity.class.getName().equals(realActivity.getClassName());
+    boolean isResolverActivity() {
+        return ResolverActivity.class.getName().equals(realActivity.getClassName());
     }
 
     ActivityRecord(ActivityManagerService _service, ProcessRecord _caller,
@@ -605,7 +605,7 @@
                     _intent.getData() == null &&
                     _intent.getType() == null &&
                     (intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) != 0 &&
-                    isNotResolverActivity()) {
+                    !isResolverActivity()) {
                 // This sure looks like a home activity!
                 mActivityType = HOME_ACTIVITY_TYPE;
             } else if (realActivity.getClassName().contains(RECENTS_PACKAGE_NAME)) {
@@ -774,7 +774,7 @@
         boolean unsent = true;
         if ((state == ActivityState.RESUMED
                 || (service.isSleeping() && task.stack != null
-                    && task.stack.topRunningActivityLocked(null) == this))
+                    && task.stack.topRunningActivityLocked() == this))
                 && app != null && app.thread != null) {
             try {
                 ArrayList<ReferrerIntent> ar = new ArrayList<>(1);
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index a796ea7..cdb00ef 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -403,9 +403,9 @@
         return mStackSupervisor.okToShowLocked(r);
     }
 
-    final ActivityRecord topRunningActivityLocked(ActivityRecord notTop) {
+    final ActivityRecord topRunningActivityLocked() {
         for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
-            ActivityRecord r = mTaskHistory.get(taskNdx).topRunningActivityLocked(notTop);
+            ActivityRecord r = mTaskHistory.get(taskNdx).topRunningActivityLocked();
             if (r != null) {
                 return r;
             }
@@ -689,7 +689,7 @@
             // NOTE: If {@link TaskRecord#topRunningActivityLocked} return is not null then it is
             // okay to show the activity when locked.
             if (mStackSupervisor.isCurrentProfileLocked(task.userId)
-                    || task.topRunningActivityLocked(null) != null) {
+                    || task.topRunningActivityLocked() != null) {
                 if (DEBUG_TASKS) Slog.d(TAG_TASKS, "switchUserLocked: stack=" + getStackId() +
                         " moving " + task + " to top");
                 mTaskHistory.remove(i);
@@ -1105,7 +1105,7 @@
                 mStackSupervisor.resumeTopActivitiesLocked(topStack, prev, null);
             } else {
                 mStackSupervisor.checkReadyForSleepLocked();
-                ActivityRecord top = topStack.topRunningActivityLocked(null);
+                ActivityRecord top = topStack.topRunningActivityLocked();
                 if (top == null || (prev != null && top != prev)) {
                     // If there are no more activities available to run,
                     // do resume anyway to start something.  Also if the top
@@ -1153,7 +1153,7 @@
         next.results = null;
         next.newIntents = null;
 
-        if (next.isHomeActivity() && next.isNotResolverActivity()) {
+        if (next.isHomeActivity()) {
             ProcessRecord app = next.task.mActivities.get(0).app;
             if (app != null && app != mService.mHomeProcess) {
                 mService.mHomeProcess = app;
@@ -1326,7 +1326,7 @@
             if (focusedStackId != HOME_STACK_ID) {
                 return true;
             }
-            ActivityRecord topHomeActivity = focusedStack.topRunningActivityLocked(null);
+            ActivityRecord topHomeActivity = focusedStack.topRunningActivityLocked();
             return topHomeActivity == null || !topHomeActivity.isHomeActivity();
         }
 
@@ -1387,7 +1387,7 @@
      */
     final void ensureActivitiesVisibleLocked(ActivityRecord starting, int configChanges,
             boolean preserveWindows) {
-        ActivityRecord top = topRunningActivityLocked(null);
+        ActivityRecord top = topRunningActivityLocked();
         if (top == null) {
             return;
         }
@@ -1643,7 +1643,7 @@
      * starting window displayed then remove that starting window. It is possible that the activity
      * in this state will never resumed in which case that starting window will be orphaned. */
     void cancelInitializingActivities() {
-        final ActivityRecord topActivity = topRunningActivityLocked(null);
+        final ActivityRecord topActivity = topRunningActivityLocked();
         boolean aboveTop = true;
         for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
             final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
@@ -1719,7 +1719,7 @@
         cancelInitializingActivities();
 
         // Find the first activity that is not finishing.
-        final ActivityRecord next = topRunningActivityLocked(null);
+        final ActivityRecord next = topRunningActivityLocked();
 
         // Remember how we'll process this pause/resume situation, and ensure
         // that the state is reset however we wind up proceeding.
@@ -2037,7 +2037,7 @@
                 // We should be all done, but let's just make sure our activity
                 // is still at the top and schedule another run if something
                 // weird happened.
-                ActivityRecord nextNext = topRunningActivityLocked(null);
+                ActivityRecord nextNext = topRunningActivityLocked();
                 if (DEBUG_SWITCH || DEBUG_STATES) Slog.i(TAG_STATES,
                         "Activity config changed during resume: " + next
                         + ", new next: " + nextNext);
@@ -2170,12 +2170,12 @@
         // Calculate maximum possible position for this task.
         int maxPosition = mTaskHistory.size();
         if (!mStackSupervisor.isCurrentProfileLocked(task.userId)
-                && task.topRunningActivityLocked(null) == null) {
+                && task.topRunningActivityLocked() == null) {
             // Put non-current user tasks below current user tasks.
             while (maxPosition > 0) {
                 final TaskRecord tmpTask = mTaskHistory.get(maxPosition - 1);
                 if (!mStackSupervisor.isCurrentProfileLocked(tmpTask.userId)
-                        || tmpTask.topRunningActivityLocked(null) == null) {
+                        || tmpTask.topRunningActivityLocked() == null) {
                     break;
                 }
                 maxPosition--;
@@ -2217,13 +2217,13 @@
         int taskNdx = mTaskHistory.size();
         final boolean notShownWhenLocked =
                 (newActivity != null && (newActivity.info.flags & FLAG_SHOW_FOR_ALL_USERS) == 0)
-                || (newActivity == null && task.topRunningActivityLocked(null) == null);
+                || (newActivity == null && task.topRunningActivityLocked() == null);
         if (!mStackSupervisor.isCurrentProfileLocked(task.userId) && notShownWhenLocked) {
             // Put non-current user tasks below current user tasks.
             while (--taskNdx >= 0) {
                 final TaskRecord tmpTask = mTaskHistory.get(taskNdx);
                 if (!mStackSupervisor.isCurrentProfileLocked(tmpTask.userId)
-                        || tmpTask.topRunningActivityLocked(null) == null) {
+                        || tmpTask.topRunningActivityLocked() == null) {
                     break;
                 }
             }
@@ -2769,7 +2769,7 @@
 
     private void adjustFocusedActivityLocked(ActivityRecord r, String reason) {
         if (mStackSupervisor.isFrontStack(this) && mService.mFocusedActivity == r) {
-            ActivityRecord next = topRunningActivityLocked(null);
+            ActivityRecord next = topRunningActivityLocked();
             final String myReason = reason + " adjustFocus";
             if (next != r) {
                 if (next != null && (mStackId == FREEFORM_WORKSPACE_STACK_ID
@@ -2812,7 +2812,7 @@
         if (stack == null) {
             return false;
         }
-        final ActivityRecord top = stack.topRunningActivityLocked(null);
+        final ActivityRecord top = stack.topRunningActivityLocked();
         if (top == null) {
             return false;
         }
@@ -2913,7 +2913,7 @@
     }
 
     final void finishTopRunningActivityLocked(ProcessRecord app, String reason) {
-        ActivityRecord r = topRunningActivityLocked(null);
+        ActivityRecord r = topRunningActivityLocked();
         if (r != null && r.app == app) {
             // If the top running activity is from this crashing
             // process, then terminate it to avoid getting in a loop.
@@ -3618,7 +3618,7 @@
     void releaseBackgroundResources(ActivityRecord r) {
         if (hasVisibleBehindActivity() &&
                 !mHandler.hasMessages(RELEASE_BACKGROUND_RESOURCES_TIMEOUT_MSG)) {
-            if (r == topRunningActivityLocked(null)) {
+            if (r == topRunningActivityLocked()) {
                 // Don't release the top activity if it has requested to run behind the next
                 // activity.
                 return;
@@ -3767,7 +3767,7 @@
 
     final void updateTransitLocked(int transit, Bundle options) {
         if (options != null) {
-            ActivityRecord r = topRunningActivityLocked(null);
+            ActivityRecord r = topRunningActivityLocked();
             if (r != null && r.state != ActivityState.RESUMED) {
                 r.updateOptionsLocked(options);
             } else {
@@ -3840,7 +3840,7 @@
         }
 
         // Set focus to the top running activity of this stack.
-        ActivityRecord r = topRunningActivityLocked(null);
+        ActivityRecord r = topRunningActivityLocked();
         mService.setFocusedActivityLocked(r, reason);
 
         if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare to front transition: task=" + tr);
@@ -4473,7 +4473,7 @@
     }
 
     ActivityRecord restartPackage(String packageName) {
-        ActivityRecord starting = topRunningActivityLocked(null);
+        ActivityRecord starting = topRunningActivityLocked();
 
         // All activities that came from the package must be
         // restarted as if there was a config change.
@@ -4545,7 +4545,8 @@
 
         if (mTaskHistory.isEmpty()) {
             if (DEBUG_STACK) Slog.i(TAG_STACK, "removeTask: removing stack=" + this);
-            if (isOnHomeDisplay()) {
+            // We only need to adjust focused stack if this stack is in focus.
+            if (isOnHomeDisplay() && mStackSupervisor.isFrontStack(this)) {
                 String myReason = reason + " leftTaskHistoryEmpty";
                 if (mFullscreen || !adjustFocusToNextVisibleStackLocked(null, myReason)) {
                     mStackSupervisor.moveHomeStackToFront(myReason);
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 6b5f205..8bd9462 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -66,7 +66,6 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.res.Configuration;
-import android.graphics.Point;
 import android.graphics.Rect;
 import android.hardware.display.DisplayManager;
 import android.hardware.display.DisplayManager.DisplayListener;
@@ -191,6 +190,9 @@
     // Force the focus to change to the stack we are moving a task to..
     static final boolean FORCE_FOCUS = true;
 
+    // Restore task from the saved recents if it can't be found in any live stack.
+    static final boolean RESTORE_FROM_RECENTS = true;
+
     // Activity actions an app cannot start if it uses a permission which is not granted.
     private static final ArrayMap<String, String> ACTION_TO_RUNTIME_PERMISSION =
             new ArrayMap<>();
@@ -541,7 +543,7 @@
     }
 
     TaskRecord anyTaskForIdLocked(int id) {
-        return anyTaskForIdLocked(id, true);
+        return anyTaskForIdLocked(id, RESTORE_FROM_RECENTS, INVALID_STACK_ID);
     }
 
     /**
@@ -549,8 +551,10 @@
      * @param id Id of the task we would like returned.
      * @param restoreFromRecents If the id was not in the active list, but was found in recents,
      *                           restore the task from recents to the active list.
+     * @param stackId The stack to restore the task to (default launch stack will be used
+     *                if stackId is {@link android.app.ActivityManager#INVALID_STACK_ID}).
      */
-    TaskRecord anyTaskForIdLocked(int id, boolean restoreFromRecents) {
+    TaskRecord anyTaskForIdLocked(int id, boolean restoreFromRecents, int stackId) {
         int numDisplays = mActivityDisplays.size();
         for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
             ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
@@ -575,7 +579,7 @@
             return task;
         }
 
-        if (!restoreRecentTaskLocked(task, INVALID_STACK_ID)) {
+        if (!restoreRecentTaskLocked(task, stackId)) {
             if (DEBUG_RECENTS) Slog.w(TAG_RECENTS,
                     "Couldn't restore task id=" + id + " found in recents");
             return null;
@@ -610,7 +614,7 @@
             if (mCurTaskId <= 0) {
                 mCurTaskId = 1;
             }
-        } while (anyTaskForIdLocked(mCurTaskId, false) != null);
+        } while (anyTaskForIdLocked(mCurTaskId, !RESTORE_FROM_RECENTS, INVALID_STACK_ID) != null);
         return mCurTaskId;
     }
 
@@ -623,7 +627,7 @@
         if (resumedActivity == null || resumedActivity.app == null) {
             resumedActivity = stack.mPausingActivity;
             if (resumedActivity == null || resumedActivity.app == null) {
-                resumedActivity = stack.topRunningActivityLocked(null);
+                resumedActivity = stack.topRunningActivityLocked();
             }
         }
         return resumedActivity;
@@ -639,7 +643,7 @@
                 if (!isFrontStack(stack)) {
                     continue;
                 }
-                ActivityRecord hr = stack.topRunningActivityLocked(null);
+                ActivityRecord hr = stack.topRunningActivityLocked();
                 if (hr != null) {
                     if (hr.app == null && app.uid == hr.info.applicationInfo.uid
                             && processName.equals(hr.processName)) {
@@ -823,7 +827,7 @@
 
     ActivityRecord topRunningActivityLocked() {
         final ActivityStack focusedStack = mFocusedStack;
-        ActivityRecord r = focusedStack.topRunningActivityLocked(null);
+        ActivityRecord r = focusedStack.topRunningActivityLocked();
         if (r != null) {
             return r;
         }
@@ -833,7 +837,7 @@
         for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
             final ActivityStack stack = stacks.get(stackNdx);
             if (stack != focusedStack && isFrontStack(stack)) {
-                r = stack.topRunningActivityLocked(null);
+                r = stack.topRunningActivityLocked();
                 if (r != null) {
                     return r;
                 }
@@ -1096,7 +1100,7 @@
                         }
                     } while (!outResult.timeout && outResult.who == null);
                 } else if (res == ActivityManager.START_TASK_TO_FRONT) {
-                    ActivityRecord r = stack.topRunningActivityLocked(null);
+                    ActivityRecord r = stack.topRunningActivityLocked();
                     if (r.nowVisible && r.state == RESUMED) {
                         outResult.timeout = false;
                         outResult.who = new ComponentName(r.info.packageName, r.info.name);
@@ -1262,7 +1266,7 @@
                         r.userId, System.identityHashCode(r),
                         task.taskId, r.shortComponentName);
             }
-            if (r.isHomeActivity() && r.isNotResolverActivity()) {
+            if (r.isHomeActivity()) {
                 // Home process is the root process of the task.
                 mService.mHomeProcess = task.mActivities.get(0).app;
             }
@@ -1799,10 +1803,14 @@
                 return container.mStack;
             }
 
-            // The fullscreen stack is the only stack that can contain any task regardless of if
-            // the task is resizeable or not. So, we let the task go in the fullscreen task if it
-            // is the focus stack.
-            if (mFocusedStack.mStackId == FULLSCREEN_WORKSPACE_STACK_ID
+            // The fullscreen stack can contain any task regardless of if the task is resizeable
+            // or not. So, we let the task go in the fullscreen task if it is the focus stack.
+            // If the freeform stack has focus, and the activity to be launched is resizeable,
+            // we can also put it in the focused stack.
+            final boolean canUseFocusedStack =
+                    mFocusedStack.mStackId == FULLSCREEN_WORKSPACE_STACK_ID
+                    || mFocusedStack.mStackId == FREEFORM_WORKSPACE_STACK_ID && r.info.resizeable;
+            if (canUseFocusedStack
                     && (!newTask || mFocusedStack.mActivityContainer.isEligibleForNewTasks())) {
                 if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
                         "computeStackFocus: Have a focused stack=" + mFocusedStack);
@@ -1855,17 +1863,11 @@
 
         boolean overrideBounds = false;
         Rect newBounds = null;
-        if (r.info.resizeable || (inTask != null && inTask.mResizeable)) {
-            if (intent.hasExtra(ActivityOptions.KEY_BOUNDS)) {
+        if (options != null && (r.info.resizeable || (inTask != null && inTask.mResizeable))) {
+            ActivityOptions opts = new ActivityOptions(options);
+            if (opts.hasBounds()) {
                 overrideBounds = true;
-                newBounds = Rect.unflattenFromString(
-                        intent.getStringExtra(ActivityOptions.KEY_BOUNDS));
-            } else if (options != null) {
-                ActivityOptions opts = new ActivityOptions(options);
-                if (opts.hasBounds()) {
-                    overrideBounds = true;
-                    newBounds = opts.getBounds();
-                }
+                newBounds = opts.getBounds();
             }
         }
 
@@ -2024,6 +2026,14 @@
             reuseTask = inTask;
         } else {
             inTask = null;
+            // Launch ResolverActivity in the source task, so that it stays in the task
+            // bounds when in freeform workspace.
+            // Also put noDisplay activities in the source task. These by itself can
+            // be placed in any task/stack, however it could launch other activities
+            // like ResolverActivity, and we want those to stay in the original task.
+            if (r.isResolverActivity() || r.noDisplay) {
+                addingToTask = true;
+            }
         }
 
         if (inTask == null) {
@@ -2971,7 +2981,7 @@
 
         Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizeStack_" + stackId);
 
-        ActivityRecord r = stack.topRunningActivityLocked(null);
+        ActivityRecord r = stack.topRunningActivityLocked();
 
         mTmpBounds.clear();
         mTmpConfigs.clear();
@@ -3058,7 +3068,7 @@
         Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
     }
 
-    void resizeTaskLocked(TaskRecord task, Rect bounds, int resizeMode) {
+    void resizeTaskLocked(TaskRecord task, Rect bounds, int resizeMode, boolean preserveWindow) {
         if (!task.mResizeable) {
             Slog.w(TAG, "resizeTask: task " + task + " not resizeable.");
             return;
@@ -3066,7 +3076,7 @@
 
         // If this is a forced resize, let it go through even if the bounds is not changing,
         // as we might need a relayout due to surface size change (to/from fullscreen).
-        final boolean forced = (resizeMode == RESIZE_MODE_FORCED);
+        final boolean forced = (resizeMode & RESIZE_MODE_FORCED) != 0;
         if (task.mBounds != null && task.mBounds.equals(bounds) && !forced) {
             // Nothing to do here...
             return;
@@ -3084,35 +3094,21 @@
             return;
         }
 
+        // Do not move the task to another stack here.
+        // This method assumes that the task is already placed in the right stack.
+        // we do not mess with that decision and we only do the resize!
+
         Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizeTask_" + task.taskId);
 
-        // The stack of a task is determined by its size (fullscreen vs non-fullscreen).
-        // Place the task in the right stack if it isn't there already based on the requested
-        // bounds.
-        int stackId = task.stack.mStackId;
-        if (bounds == null && stackId != FULLSCREEN_WORKSPACE_STACK_ID) {
-            stackId = FULLSCREEN_WORKSPACE_STACK_ID;
-        } else if (bounds != null
-                && stackId != FREEFORM_WORKSPACE_STACK_ID && stackId != DOCKED_STACK_ID) {
-            stackId = FREEFORM_WORKSPACE_STACK_ID;
-        }
-        final boolean changedStacks = stackId != task.stack.mStackId;
-        if (changedStacks) {
-            moveTaskToStackUncheckedLocked(task, stackId, ON_TOP, !FORCE_FOCUS, "resizeTask");
-        }
-
         final Configuration overrideConfig =  task.updateOverrideConfiguration(bounds);
-        // This variable holds information whether the configuration didn't change in a signficant
+        // This variable holds information whether the configuration didn't change in a significant
         // way and the activity was kept the way it was. If it's false, it means the activity had
         // to be relaunched due to configuration change.
         boolean kept = true;
         if (overrideConfig != null) {
-            ActivityRecord r = task.topRunningActivityLocked(null);
+            ActivityRecord r = task.topRunningActivityLocked();
             if (r != null) {
                 final ActivityStack stack = task.stack;
-                final boolean preserveWindow = !changedStacks &&
-                        (resizeMode == RESIZE_MODE_USER
-                        || resizeMode == RESIZE_MODE_SYSTEM_SCREEN_ROTATION);
                 kept = stack.ensureActivityConfigurationLocked(r, 0, preserveWindow);
                 // All other activities must be made visible with their correct configuration.
                 ensureActivitiesVisibleLocked(r, 0, !PRESERVE_WINDOWS);
@@ -3153,7 +3149,7 @@
      * Restores a recent task to a stack
      * @param task The recent task to be restored.
      * @param stackId The stack to restore the task to (default launch stack will be used
-     *                if stackId is invalid).
+     *                if stackId is {@link android.app.ActivityManager#INVALID_STACK_ID}).
      * @return true if the task has been restored successfully.
      */
     private boolean restoreRecentTaskLocked(TaskRecord task, int stackId) {
@@ -3204,7 +3200,7 @@
      * @param reason Reason the task is been moved.
      * @return The stack the task was moved to.
      */
-    private ActivityStack moveTaskToStackUncheckedLocked(
+    ActivityStack moveTaskToStackUncheckedLocked(
             TaskRecord task, int stackId, boolean toTop, boolean forceFocus, String reason) {
         final ActivityRecord r = task.getTopActivity();
         final boolean wasFocused = isFrontStack(task.stack) && (topRunningActivityLocked() == r);
@@ -3252,7 +3248,7 @@
             // and then add a new one. This call will tell window manager about this, so it can
             // preserve the old window until the new one is drawn. This prevents having a gap
             // between the removal and addition, in which no window is visible. We also want the
-            // entrace of the new window to be properly animated.
+            // entrance of the new window to be properly animated.
             ActivityRecord r = task.getTopActivity();
             mWindowManager.setReplacingWindow(r.appToken, true /* animate */);
         }
@@ -3261,12 +3257,15 @@
 
         // 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);
+            resizeTaskLocked(task, stack.mBounds,
+                    RESIZE_MODE_SYSTEM, !PRESERVE_WINDOWS);
         } else if (stackId == FREEFORM_WORKSPACE_STACK_ID
                 && task.mBounds == null && task.mLastNonFullscreenBounds != null) {
-            resizeTaskLocked(task, task.mLastNonFullscreenBounds, RESIZE_MODE_SYSTEM);
+            resizeTaskLocked(task, task.mLastNonFullscreenBounds,
+                    RESIZE_MODE_SYSTEM, !PRESERVE_WINDOWS);
         } else if (stackId == DOCKED_STACK_ID) {
-            resizeTaskLocked(task, stack.mBounds, RESIZE_MODE_SYSTEM);
+            resizeTaskLocked(task, stack.mBounds,
+                    RESIZE_MODE_SYSTEM, !PRESERVE_WINDOWS);
         }
 
         // The task might have already been running and its visibility needs to be synchronized with
@@ -3738,7 +3737,7 @@
             final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
             for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
                 final ActivityStack stack = stacks.get(stackNdx);
-                final ActivityRecord r = stack.topRunningActivityLocked(null);
+                final ActivityRecord r = stack.topRunningActivityLocked();
                 final ActivityState state = r == null ? DESTROYED : r.state;
                 if (isFrontStack(stack)) {
                     if (r == null) Slog.e(TAG,
diff --git a/services/core/java/com/android/server/am/CompatModePackages.java b/services/core/java/com/android/server/am/CompatModePackages.java
index c36fd06..26264e5 100644
--- a/services/core/java/com/android/server/am/CompatModePackages.java
+++ b/services/core/java/com/android/server/am/CompatModePackages.java
@@ -196,7 +196,7 @@
     }
 
     public boolean getFrontActivityAskCompatModeLocked() {
-        ActivityRecord r = mService.getFocusedStack().topRunningActivityLocked(null);
+        ActivityRecord r = mService.getFocusedStack().topRunningActivityLocked();
         if (r == null) {
             return false;
         }
@@ -208,7 +208,7 @@
     }
 
     public void setFrontActivityAskCompatModeLocked(boolean ask) {
-        ActivityRecord r = mService.getFocusedStack().topRunningActivityLocked(null);
+        ActivityRecord r = mService.getFocusedStack().topRunningActivityLocked();
         if (r != null) {
             setPackageAskCompatModeLocked(r.packageName, ask);
         }
@@ -230,7 +230,7 @@
     }
 
     public int getFrontActivityScreenCompatModeLocked() {
-        ActivityRecord r = mService.getFocusedStack().topRunningActivityLocked(null);
+        ActivityRecord r = mService.getFocusedStack().topRunningActivityLocked();
         if (r == null) {
             return ActivityManager.COMPAT_MODE_UNKNOWN;
         }
@@ -238,7 +238,7 @@
     }
 
     public void setFrontActivityScreenCompatModeLocked(int mode) {
-        ActivityRecord r = mService.getFocusedStack().topRunningActivityLocked(null);
+        ActivityRecord r = mService.getFocusedStack().topRunningActivityLocked();
         if (r == null) {
             Slog.w(TAG, "setFrontActivityScreenCompatMode failed: no top activity");
             return;
diff --git a/services/core/java/com/android/server/am/TaskPersister.java b/services/core/java/com/android/server/am/TaskPersister.java
index aa154a7..871331b 100644
--- a/services/core/java/com/android/server/am/TaskPersister.java
+++ b/services/core/java/com/android/server/am/TaskPersister.java
@@ -35,6 +35,7 @@
 import android.util.Xml;
 import android.os.Process;
 
+import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.XmlUtils;
 
@@ -330,7 +331,7 @@
         return null;
     }
 
-    ArrayList<TaskRecord> restoreTasksLocked() {
+    ArrayList<TaskRecord> restoreTasksLocked(final int [] validUserIds) {
         final ArrayList<TaskRecord> tasks = new ArrayList<TaskRecord>();
         ArraySet<Integer> recoveredTaskIds = new ArraySet<Integer>();
 
@@ -362,15 +363,18 @@
                             if (DEBUG) Slog.d(TAG, "restoreTasksLocked: restored task=" +
                                     task);
                             if (task != null) {
-                                task.isPersistable = true;
                                 // XXX Don't add to write queue... there is no reason to write
                                 // out the stuff we just read, if we don't write it we will
                                 // read the same thing again.
                                 //mWriteQueue.add(new TaskWriteQueueItem(task));
-                                tasks.add(task);
                                 final int taskId = task.taskId;
-                                recoveredTaskIds.add(taskId);
                                 mStackSupervisor.setNextTaskId(taskId);
+                                // Check if it's a valid user id. Don't add tasks for removed users.
+                                if (ArrayUtils.contains(validUserIds, task.userId)) {
+                                    task.isPersistable = true;
+                                    tasks.add(task);
+                                    recoveredTaskIds.add(taskId);
+                                }
                             } else {
                                 Slog.e(TAG, "Unable to restore taskFile=" + taskFile + ": " +
                                         fileToString(taskFile));
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 7e14b2b..77dbad4 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -557,11 +557,11 @@
         return null;
     }
 
-    ActivityRecord topRunningActivityLocked(ActivityRecord notTop) {
+    ActivityRecord topRunningActivityLocked() {
         if (stack != null) {
             for (int activityNdx = mActivities.size() - 1; activityNdx >= 0; --activityNdx) {
                 ActivityRecord r = mActivities.get(activityNdx);
-                if (!r.finishing && r != notTop && stack.okToShowLocked(r)) {
+                if (!r.finishing && stack.okToShowLocked(r)) {
                     return r;
                 }
             }
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index e49a7e4..c4b57f1 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -501,7 +501,6 @@
     private volatile IRingtonePlayer mRingtonePlayer;
 
     private int mDeviceOrientation = Configuration.ORIENTATION_UNDEFINED;
-    private int mDeviceRotation = Surface.ROTATION_0;
 
     // Request to override default use of A2DP for media.
     private boolean mBluetoothA2dpEnabled;
@@ -545,8 +544,6 @@
     // If absolute volume is supported in AVRCP device
     private boolean mAvrcpAbsVolSupported = false;
 
-    private AudioOrientationEventListener mOrientationListener;
-
     private static Long mLastDeviceConnectMsgTime = new Long(0);
 
     private AudioManagerInternal.RingerModeDelegate mRingerModeDelegate;
@@ -669,15 +666,7 @@
         }
         mMonitorRotation = SystemProperties.getBoolean("ro.audio.monitorRotation", false);
         if (mMonitorRotation) {
-            mDeviceRotation = ((WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE))
-                    .getDefaultDisplay().getRotation();
-            Log.v(TAG, "monitoring device rotation, initial=" + mDeviceRotation);
-
-            mOrientationListener = new AudioOrientationEventListener(mContext);
-            mOrientationListener.enable();
-
-            // initialize rotation in AudioSystem
-            setRotationForAudioSystem();
+            RotationHelper.init(mContext, mAudioHandler);
         }
 
         context.registerReceiverAsUser(mReceiver, UserHandle.ALL, intentFilter, null, null);
@@ -805,7 +794,7 @@
             setOrientationForAudioSystem();
         }
         if (mMonitorRotation) {
-            setRotationForAudioSystem();
+            RotationHelper.updateOrientation();
         }
 
         synchronized (mBluetoothA2dpEnabledLock) {
@@ -1058,25 +1047,6 @@
         return (index * mStreamStates[dstStream].getMaxIndex() + mStreamStates[srcStream].getMaxIndex() / 2) / mStreamStates[srcStream].getMaxIndex();
     }
 
-    private class AudioOrientationEventListener
-            extends OrientationEventListener {
-        public AudioOrientationEventListener(Context context) {
-            super(context);
-        }
-
-        @Override
-        public void onOrientationChanged(int orientation) {
-            //Even though we're responding to phone orientation events,
-            //use display rotation so audio stays in sync with video/dialogs
-            int newRotation = ((WindowManager) mContext.getSystemService(
-                    Context.WINDOW_SERVICE)).getDefaultDisplay().getRotation();
-            if (newRotation != mDeviceRotation) {
-                mDeviceRotation = newRotation;
-                setRotationForAudioSystem();
-            }
-        }
-    }
-
     ///////////////////////////////////////////////////////////////////////////
     // IPC methods
     ///////////////////////////////////////////////////////////////////////////
@@ -5066,14 +5036,13 @@
                 }
             } else if (action.equals(Intent.ACTION_SCREEN_ON)) {
                 if (mMonitorRotation) {
-                    mOrientationListener.onOrientationChanged(0); //argument is ignored anyway
-                    mOrientationListener.enable();
+                    RotationHelper.enable();
                 }
                 AudioSystem.setParameters("screen_state=on");
             } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
                 if (mMonitorRotation) {
                     //reduce wakeups (save current) by only listening when display is on
-                    mOrientationListener.disable();
+                    RotationHelper.disable();
                 }
                 AudioSystem.setParameters("screen_state=off");
             } else if (action.equals(Intent.ACTION_CONFIGURATION_CHANGED)) {
@@ -5322,6 +5291,7 @@
         }
     }
 
+    //TODO move to an external "orientation helper" class
     private void setOrientationForAudioSystem() {
         switch (mDeviceOrientation) {
             case Configuration.ORIENTATION_LANDSCAPE:
@@ -5345,26 +5315,6 @@
         }
     }
 
-    private void setRotationForAudioSystem() {
-        switch (mDeviceRotation) {
-            case Surface.ROTATION_0:
-                AudioSystem.setParameters("rotation=0");
-                break;
-            case Surface.ROTATION_90:
-                AudioSystem.setParameters("rotation=90");
-                break;
-            case Surface.ROTATION_180:
-                AudioSystem.setParameters("rotation=180");
-                break;
-            case Surface.ROTATION_270:
-                AudioSystem.setParameters("rotation=270");
-                break;
-            default:
-                Log.e(TAG, "Unknown device rotation");
-        }
-    }
-
-
     // Handles request to override default use of A2DP for media.
     // Must be called synchronized on mConnectedDevices
     public void setBluetoothA2dpOnInt(boolean on) {
diff --git a/services/core/java/com/android/server/audio/RotationHelper.java b/services/core/java/com/android/server/audio/RotationHelper.java
new file mode 100644
index 0000000..359cc36
--- /dev/null
+++ b/services/core/java/com/android/server/audio/RotationHelper.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.audio;
+
+import android.content.Context;
+import android.media.AudioSystem;
+import android.os.Handler;
+import android.util.Log;
+import android.view.OrientationEventListener;
+import android.view.Surface;
+import android.view.WindowManager;
+
+import com.android.server.policy.WindowOrientationListener;
+
+/**
+ * Class to handle device rotation events for AudioService, and forward device rotation
+ * to the audio HALs through AudioSystem.
+ *
+ * The role of this class is to monitor device orientation changes, and upon rotation,
+ * verify the UI orientation. In case of a change, send the new orientation, in increments
+ * of 90deg, through AudioSystem.
+ *
+ * Note that even though we're responding to device orientation events, we always
+ * query the display rotation so audio stays in sync with video/dialogs. This is
+ * done with .getDefaultDisplay().getRotation() from WINDOW_SERVICE.
+ */
+class RotationHelper {
+
+    private static final String TAG = "AudioService.RotationHelper";
+
+    private static AudioOrientationListener sOrientationListener;
+    private static AudioWindowOrientationListener sWindowOrientationListener;
+
+    private static final Object sRotationLock = new Object();
+    private static int sDeviceRotation = Surface.ROTATION_0; // R/W synchronized on sRotationLock
+
+    private static Context sContext;
+
+    /**
+     * post conditions:
+     * - (sWindowOrientationListener != null) xor (sOrientationListener != null)
+     * - sWindowOrientationListener xor sOrientationListener is enabled
+     * - sContext != null
+     */
+    static void init(Context context, Handler handler) {
+        if (context == null) {
+            throw new IllegalArgumentException("Invalid null context");
+        }
+        sContext = context;
+        sWindowOrientationListener = new AudioWindowOrientationListener(context, handler);
+        sWindowOrientationListener.enable();
+        if (!sWindowOrientationListener.canDetectOrientation()) {
+            // cannot use com.android.server.policy.WindowOrientationListener, revert to public
+            // orientation API
+            Log.i(TAG, "Not using WindowOrientationListener, reverting to OrientationListener");
+            sWindowOrientationListener.disable();
+            sWindowOrientationListener = null;
+            sOrientationListener = new AudioOrientationListener(context);
+            sOrientationListener.enable();
+        }
+    }
+
+    static void enable() {
+        if (sWindowOrientationListener != null) {
+            sWindowOrientationListener.enable();
+        } else {
+            sOrientationListener.enable();
+        }
+        updateOrientation();
+    }
+
+    static void disable() {
+        if (sWindowOrientationListener != null) {
+            sWindowOrientationListener.disable();
+        } else {
+            sOrientationListener.disable();
+        }
+    }
+
+    /**
+     * Query current display rotation and publish the change if any.
+     */
+    static void updateOrientation() {
+        // Even though we're responding to device orientation events,
+        // use display rotation so audio stays in sync with video/dialogs
+        int newRotation = ((WindowManager) sContext.getSystemService(
+                Context.WINDOW_SERVICE)).getDefaultDisplay().getRotation();
+        synchronized(sRotationLock) {
+            if (newRotation != sDeviceRotation) {
+                sDeviceRotation = newRotation;
+                publishRotation(sDeviceRotation);
+            }
+        }
+    }
+
+    private static void publishRotation(int rotation) {
+        Log.v(TAG, "publishing device rotation =" + rotation + " (x90deg)");
+        switch (rotation) {
+            case Surface.ROTATION_0:
+                AudioSystem.setParameters("rotation=0");
+                break;
+            case Surface.ROTATION_90:
+                AudioSystem.setParameters("rotation=90");
+                break;
+            case Surface.ROTATION_180:
+                AudioSystem.setParameters("rotation=180");
+                break;
+            case Surface.ROTATION_270:
+                AudioSystem.setParameters("rotation=270");
+                break;
+            default:
+                Log.e(TAG, "Unknown device rotation");
+        }
+    }
+
+    /**
+     * Uses android.view.OrientationEventListener
+     */
+    final static class AudioOrientationListener extends OrientationEventListener {
+        AudioOrientationListener(Context context) {
+            super(context);
+        }
+
+        @Override
+        public void onOrientationChanged(int orientation) {
+            updateOrientation();
+        }
+    }
+
+    /**
+     * Uses com.android.server.policy.WindowOrientationListener
+     */
+    final static class AudioWindowOrientationListener extends WindowOrientationListener {
+        private static RotationCheckThread sRotationCheckThread;
+
+        AudioWindowOrientationListener(Context context, Handler handler) {
+            super(context, handler);
+        }
+
+        public void onProposedRotationChanged(int rotation) {
+            updateOrientation();
+            if (sRotationCheckThread != null) {
+                sRotationCheckThread.endCheck();
+            }
+            sRotationCheckThread = new RotationCheckThread();
+            sRotationCheckThread.beginCheck();
+        }
+    }
+
+    /**
+     * When com.android.server.policy.WindowOrientationListener report an orientation change,
+     * the UI may not have rotated yet. This thread polls with gradually increasing delays
+     * the new orientation.
+     */
+    final static class RotationCheckThread extends Thread {
+        // how long to wait between each rotation check
+        private final int[] WAIT_TIMES_MS = { 10, 20, 50, 100, 100, 200, 200, 500 };
+        private int mWaitCounter;
+        private final Object mCounterLock = new Object();
+
+        RotationCheckThread() {
+            super("RotationCheck");
+        }
+
+        void beginCheck() {
+            synchronized(mCounterLock) {
+                mWaitCounter = 0;
+            }
+            try {
+                start();
+            } catch (IllegalStateException e) { }
+        }
+
+        void endCheck() {
+            synchronized(mCounterLock) {
+                mWaitCounter = WAIT_TIMES_MS.length;
+            }
+        }
+
+        public void run() {
+            while (mWaitCounter < WAIT_TIMES_MS.length) {
+                int waitTimeMs;
+                synchronized(mCounterLock) {
+                    waitTimeMs = mWaitCounter < WAIT_TIMES_MS.length ?
+                            WAIT_TIMES_MS[mWaitCounter] : 0;
+                    mWaitCounter++;
+                }
+                try {
+                    if (waitTimeMs > 0) {
+                        sleep(waitTimeMs);
+                        updateOrientation();
+                    }
+                } catch (InterruptedException e) { }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index ecda36a..06bd583 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -443,13 +443,16 @@
     }
 
     /**
-     * A job is rescheduled with exponential back-off if the client requests this from their
-     * execution logic.
-     * A caveat is for idle-mode jobs, for which the idle-mode constraint will usurp the
-     * timeliness of the reschedule. For an idle-mode job, no deadline is given.
+     * Reschedules the given job based on the job's backoff policy. It doesn't make sense to
+     * specify an override deadline on a failed job (the failed job will run even though it's not
+     * ready), so we reschedule it with {@link JobStatus#NO_LATEST_RUNTIME}, but specify that any
+     * ready job with {@link JobStatus#numFailures} > 0 will be executed.
+     *
      * @param failureToReschedule Provided job status that we will reschedule.
      * @return A newly instantiated JobStatus with the same constraints as the last job except
      * with adjusted timing constraints.
+     *
+     * @see JobHandler#maybeQueueReadyJobsForExecutionLockedH
      */
     private JobStatus getRescheduleJobForFailure(JobStatus failureToReschedule) {
         final long elapsedNowMillis = SystemClock.elapsedRealtime();
@@ -479,8 +482,9 @@
     }
 
     /**
-     * Called after a periodic has executed so we can to re-add it. We take the last execution time
-     * of the job to be the time of completion (i.e. the time at which this function is called).
+     * Called after a periodic has executed so we can reschedule it. We take the last execution
+     * time of the job to be the time of completion (i.e. the time at which this function is
+     * called).
      * This could be inaccurate b/c the job can run for as long as
      * {@link com.android.server.job.JobServiceContext#EXECUTING_TIMESLICE_MILLIS}, but will lead
      * to underscheduling at least, rather than if we had taken the last execution time to be the
@@ -491,7 +495,12 @@
     private JobStatus getRescheduleJobForPeriodic(JobStatus periodicToReschedule) {
         final long elapsedNow = SystemClock.elapsedRealtime();
         // Compute how much of the period is remaining.
-        long runEarly = Math.max(periodicToReschedule.getLatestRunTimeElapsed() - elapsedNow, 0);
+        long runEarly = 0L;
+
+        // If this periodic was rescheduled it won't have a deadline.
+        if (periodicToReschedule.hasDeadlineConstraint()) {
+            runEarly = Math.max(periodicToReschedule.getLatestRunTimeElapsed() - elapsedNow, 0L);
+        }
         long newEarliestRunTimeElapsed = elapsedNow + runEarly;
         long period = periodicToReschedule.getJob().getIntervalMillis();
         long newLatestRuntimeElapsed = newEarliestRunTimeElapsed + period;
diff --git a/services/core/java/com/android/server/job/JobServiceContext.java b/services/core/java/com/android/server/job/JobServiceContext.java
index d26319b..d7fafe3 100644
--- a/services/core/java/com/android/server/job/JobServiceContext.java
+++ b/services/core/java/com/android/server/job/JobServiceContext.java
@@ -68,7 +68,7 @@
     private static final int defaultMaxActiveJobsPerService =
             ActivityManager.isLowRamDeviceStatic() ? 1 : 3;
     /** Amount of time a job is allowed to execute for before being considered timed-out. */
-    private static final long EXECUTING_TIMESLICE_MILLIS = 10 * 60 * 1000;
+    private static final long EXECUTING_TIMESLICE_MILLIS = 10 * 60 * 1000;  // 10mins.
     /** Amount of time the JobScheduler will wait for a response from an app for a message. */
     private static final long OP_TIMEOUT_MILLIS = 8 * 1000;
 
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 7b15aad..e8e46ef 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -1602,41 +1602,45 @@
         }
 
         @Override
-        public AutomaticZenRule getAutomaticZenRule(String name) throws RemoteException {
+        public AutomaticZenRule getAutomaticZenRule(String id) throws RemoteException {
+            Preconditions.checkNotNull(id, "Id is null");
             enforcePolicyAccess(Binder.getCallingUid(), "getAutomaticZenRule");
-            return mZenModeHelper.getAutomaticZenRule(name);
+            return mZenModeHelper.getAutomaticZenRule(id);
         }
 
         @Override
-        public boolean addOrUpdateAutomaticZenRule(AutomaticZenRule automaticZenRule)
+        public AutomaticZenRule addAutomaticZenRule(AutomaticZenRule automaticZenRule)
                 throws RemoteException {
             Preconditions.checkNotNull(automaticZenRule, "automaticZenRule is null");
             Preconditions.checkNotNull(automaticZenRule.getName(), "Name is null");
             Preconditions.checkNotNull(automaticZenRule.getOwner(), "Owner is null");
             Preconditions.checkNotNull(automaticZenRule.getConditionId(), "ConditionId is null");
-            enforcePolicyAccess(Binder.getCallingUid(), "addOrUpdateZenModeRule");
+            enforcePolicyAccess(Binder.getCallingUid(), "addAutomaticZenRule");
 
-            return mZenModeHelper.addOrUpdateAutomaticZenRule(automaticZenRule,
-                    "addOrUpdateAutomaticZenRule");
+            return mZenModeHelper.addAutomaticZenRule(automaticZenRule,
+                    "addAutomaticZenRule");
         }
 
         @Override
-        public boolean renameAutomaticZenRule(String oldName, String newName) {
-            Preconditions.checkNotNull(oldName, "oldName is null");
-            Preconditions.checkNotNull(newName, "newName is null");
-            enforcePolicyAccess(Binder.getCallingUid(), "renameAutomaticZenRule");
+        public boolean updateAutomaticZenRule(AutomaticZenRule automaticZenRule)
+                throws RemoteException {
+            Preconditions.checkNotNull(automaticZenRule, "automaticZenRule is null");
+            Preconditions.checkNotNull(automaticZenRule.getName(), "Name is null");
+            Preconditions.checkNotNull(automaticZenRule.getOwner(), "Owner is null");
+            Preconditions.checkNotNull(automaticZenRule.getConditionId(), "ConditionId is null");
+            enforcePolicyAccess(Binder.getCallingUid(), "updateAutomaticZenRule");
 
-            return mZenModeHelper.renameAutomaticZenRule(
-                    oldName, newName, "renameAutomaticZenRule");
+            return mZenModeHelper.updateAutomaticZenRule(automaticZenRule,
+                    "updateAutomaticZenRule");
         }
 
         @Override
-        public boolean removeAutomaticZenRule(String name) throws RemoteException {
-            Preconditions.checkNotNull(name, "Name is null");
+        public boolean removeAutomaticZenRule(String id) throws RemoteException {
+            Preconditions.checkNotNull(id, "Id is null");
             // Verify that they can modify zen rules.
             enforcePolicyAccess(Binder.getCallingUid(), "removeAutomaticZenRule");
 
-            return mZenModeHelper.removeAutomaticZenRule(name, "removeAutomaticZenRule");
+            return mZenModeHelper.removeAutomaticZenRule(id, "removeAutomaticZenRule");
         }
 
         @Override
diff --git a/services/core/java/com/android/server/notification/ZenModeConditions.java b/services/core/java/com/android/server/notification/ZenModeConditions.java
index b89a654..c2e4349 100644
--- a/services/core/java/com/android/server/notification/ZenModeConditions.java
+++ b/services/core/java/com/android/server/notification/ZenModeConditions.java
@@ -104,7 +104,7 @@
     public void onServiceAdded(ComponentName component) {
         if (DEBUG) Log.d(TAG, "onServiceAdded " + component);
         if (isAutomaticActive(component)) {
-            mHelper.setConfig(mHelper.getConfig(), "zmc.onServiceAdded");
+            mHelper.setConfigAsync(mHelper.getConfig(), "zmc.onServiceAdded");
         }
     }
 
@@ -120,7 +120,7 @@
             updated |= updateSnoozing(automaticRule);
         }
         if (updated) {
-            mHelper.setConfig(config, "conditionChanged");
+            mHelper.setConfigAsync(config, "conditionChanged");
         }
     }
 
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 4d41e3a..76c6443 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -49,6 +49,7 @@
 import android.service.notification.ZenModeConfig.EventInfo;
 import android.service.notification.ZenModeConfig.ScheduleInfo;
 import android.service.notification.ZenModeConfig.ZenRule;
+import android.text.TextUtils;
 import android.util.Log;
 import android.util.SparseArray;
 
@@ -211,82 +212,69 @@
         return rules;
     }
 
-    public AutomaticZenRule getAutomaticZenRule(String name) {
+    public AutomaticZenRule getAutomaticZenRule(String id) {
         if (mConfig == null) return null;
-        for(ZenRule rule : mConfig.automaticRules.values()) {
-            if (canManageAutomaticZenRule(rule) && rule.name.equals(name)) {
-                return createAutomaticZenRule(rule);
-            }
+        ZenRule rule = mConfig.automaticRules.get(id);
+        if (rule == null) return null;
+        if (canManageAutomaticZenRule(rule)) {
+             return createAutomaticZenRule(rule);
         }
         return null;
     }
 
-    public boolean addOrUpdateAutomaticZenRule(AutomaticZenRule automaticZenRule, String reason) {
+    public AutomaticZenRule addAutomaticZenRule(AutomaticZenRule automaticZenRule, String reason) {
+        if (mConfig == null) return null;
+        if (DEBUG) {
+          Log.d(TAG, "addAutomaticZenRule zenRule= " + automaticZenRule + " reason=" +reason);
+        }
+        if (!TextUtils.isEmpty(automaticZenRule.getId())) {
+            throw new IllegalArgumentException("Rule already exists");
+        }
+        final ZenModeConfig newConfig = mConfig.copy();
+        ZenRule rule = new ZenRule();
+        populateZenRule(automaticZenRule, rule, true);
+        newConfig.automaticRules.put(rule.id, rule);
+        if (setConfig(newConfig, reason, true)) {
+            return createAutomaticZenRule(rule);
+        } else {
+            return null;
+        }
+    }
+
+    public boolean updateAutomaticZenRule(AutomaticZenRule automaticZenRule, String reason) {
         if (mConfig == null) return false;
         if (DEBUG) {
-            Log.d(TAG, "addOrUpdateAutomaticZenRule zenRule=" + automaticZenRule
+            Log.d(TAG, "updateAutomaticZenRule zenRule=" + automaticZenRule
                     + " reason=" + reason);
         }
         final ZenModeConfig newConfig = mConfig.copy();
-        String ruleId = findMatchingRuleId(newConfig, automaticZenRule.getName());
+        final String ruleId = automaticZenRule.getId();
         ZenModeConfig.ZenRule rule = new ZenModeConfig.ZenRule();
         if (ruleId == null) {
-            ruleId = newConfig.newRuleId();
-            rule.name = automaticZenRule.getName();
-            rule.component = automaticZenRule.getOwner();
+            throw new IllegalArgumentException("Rule doesn't exist");
         } else {
             rule = newConfig.automaticRules.get(ruleId);
-            if (!canManageAutomaticZenRule(rule)) {
+            if (rule == null || !canManageAutomaticZenRule(rule)) {
                 throw new SecurityException(
                         "Cannot update rules not owned by your condition provider");
             }
         }
-        if (rule.enabled != automaticZenRule.isEnabled()) {
-            rule.snoozing = false;
-        }
-        rule.condition = null;
-        rule.conditionId = automaticZenRule.getConditionId();
-        rule.enabled = automaticZenRule.isEnabled();
-        rule.zenMode = NotificationManager.zenModeFromInterruptionFilter(
-                automaticZenRule.getInterruptionFilter(), Global.ZEN_MODE_OFF);
+        populateZenRule(automaticZenRule, rule, false);
         newConfig.automaticRules.put(ruleId, rule);
         return setConfig(newConfig, reason, true);
     }
 
-    public boolean renameAutomaticZenRule(String oldName, String newName, String reason) {
+    public boolean removeAutomaticZenRule(String id, String reason) {
         if (mConfig == null) return false;
-        if (DEBUG) {
-            Log.d(TAG, "renameAutomaticZenRule oldName=" + oldName + "  newName=" + newName
-                    + " reason=" + reason);
-        }
         final ZenModeConfig newConfig = mConfig.copy();
-        String ruleId = findMatchingRuleId(newConfig, oldName);
-        if (ruleId == null) {
-            return false;
+        ZenRule rule = newConfig.automaticRules.get(id);
+        if (rule == null) return false;
+        if (canManageAutomaticZenRule(rule)) {
+            newConfig.automaticRules.remove(id);
+            if (DEBUG) Log.d(TAG, "removeZenRule zenRule=" + id + " reason=" + reason);
         } else {
-            ZenRule rule = newConfig.automaticRules.get(ruleId);
-            if (!canManageAutomaticZenRule(rule)) {
-                throw new SecurityException(
-                        "Cannot update rules not owned by your condition provider");
-            }
-            rule.name = newName;
-            return setConfig(newConfig, reason, true);
-        }
-    }
-
-    public boolean removeAutomaticZenRule(String name, String reason) {
-        if (mConfig == null) return false;
-        final ZenModeConfig newConfig = mConfig.copy();
-        String ruleId = findMatchingRuleId(newConfig, name);
-        if (ruleId != null) {
-            ZenRule rule = newConfig.automaticRules.get(ruleId);
-            if (canManageAutomaticZenRule(rule)) {
-                newConfig.automaticRules.remove(ruleId);
-                if (DEBUG) Log.d(TAG, "removeZenRule zenRule=" + name + " reason=" + reason);
-            } else {
-                throw new SecurityException(
-                        "Cannot delete rules not owned by your condition provider");
-            }
+            throw new SecurityException(
+                     "Cannot delete rules not owned by your condition provider");
         }
         return setConfig(newConfig, reason, true);
     }
@@ -310,9 +298,28 @@
         }
     }
 
+    private void populateZenRule(AutomaticZenRule automaticZenRule, ZenRule rule, boolean isNew) {
+        if (isNew) {
+            rule.id = ZenModeConfig.newRuleId();
+            rule.creationTime = System.currentTimeMillis();
+            rule.component = automaticZenRule.getOwner();
+        }
+
+        if (rule.enabled != automaticZenRule.isEnabled()) {
+            rule.snoozing = false;
+        }
+        rule.name = automaticZenRule.getName();
+        rule.condition = null;
+        rule.conditionId = automaticZenRule.getConditionId();
+        rule.enabled = automaticZenRule.isEnabled();
+        rule.zenMode = NotificationManager.zenModeFromInterruptionFilter(
+                automaticZenRule.getInterruptionFilter(), Global.ZEN_MODE_OFF);
+    }
+
     private AutomaticZenRule createAutomaticZenRule(ZenRule rule) {
         return new AutomaticZenRule(rule.name, rule.component, rule.conditionId,
-                NotificationManager.zenModeToInterruptionFilter(rule.zenMode), rule.enabled);
+                NotificationManager.zenModeToInterruptionFilter(rule.zenMode), rule.enabled,
+                rule.id, rule.creationTime);
     }
 
     public void setManualZenMode(int zenMode, Uri conditionId, String reason) {
@@ -344,15 +351,6 @@
         setConfig(newConfig, reason, setRingerMode);
     }
 
-    private String findMatchingRuleId(ZenModeConfig config, String ruleName) {
-        for (String ruleId : config.automaticRules.keySet()) {
-            if (config.automaticRules.get(ruleId).name.equals(ruleName)) {
-                return ruleId;
-            }
-        }
-        return null;
-    }
-
     public void dump(PrintWriter pw, String prefix) {
         pw.print(prefix); pw.print("mZenMode=");
         pw.println(Global.zenModeToString(mZenMode));
@@ -446,6 +444,10 @@
         return setConfig(config, reason, true /*setRingerMode*/);
     }
 
+    public void setConfigAsync(ZenModeConfig config, String reason) {
+        mHandler.postSetConfig(config, reason);
+    }
+
     private boolean setConfig(ZenModeConfig config, String reason, boolean setRingerMode) {
         final long identity = Binder.clearCallingIdentity();
         try {
@@ -640,7 +642,9 @@
         rule1.conditionId = ZenModeConfig.toScheduleConditionId(weeknights);
         rule1.zenMode = Global.ZEN_MODE_ALARMS;
         rule1.component = ScheduleConditionProvider.COMPONENT;
-        config.automaticRules.put(config.newRuleId(), rule1);
+        rule1.id = ZenModeConfig.newRuleId();
+        rule1.creationTime = System.currentTimeMillis();
+        config.automaticRules.put(rule1.id, rule1);
 
         final ScheduleInfo weekends = new ScheduleInfo();
         weekends.days = ZenModeConfig.WEEKEND_DAYS;
@@ -654,7 +658,9 @@
         rule2.conditionId = ZenModeConfig.toScheduleConditionId(weekends);
         rule2.zenMode = Global.ZEN_MODE_ALARMS;
         rule2.component = ScheduleConditionProvider.COMPONENT;
-        config.automaticRules.put(config.newRuleId(), rule2);
+        rule2.id = ZenModeConfig.newRuleId();
+        rule2.creationTime = System.currentTimeMillis();
+        config.automaticRules.put(rule2.id, rule2);
     }
 
     private void appendDefaultEventRules(ZenModeConfig config) {
@@ -669,7 +675,9 @@
         rule.conditionId = ZenModeConfig.toEventConditionId(events);
         rule.zenMode = Global.ZEN_MODE_ALARMS;
         rule.component = EventConditionProvider.COMPONENT;
-        config.automaticRules.put(config.newRuleId(), rule);
+        rule.id = ZenModeConfig.newRuleId();
+        rule.creationTime = System.currentTimeMillis();
+        config.automaticRules.put(rule.id, rule);
     }
 
     private static int zenSeverity(int zen) {
@@ -710,7 +718,7 @@
                 rule.zenMode = v1.sleepNone ? Global.ZEN_MODE_NO_INTERRUPTIONS
                         : Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
                 rule.component = ScheduleConditionProvider.COMPONENT;
-                rt.automaticRules.put(rt.newRuleId(), rule);
+                rt.automaticRules.put(ZenModeConfig.newRuleId(), rule);
             } else {
                 Log.i(TAG, "No existing V1 downtime found, generating default schedules");
                 appendDefaultScheduleRules(rt);
@@ -883,6 +891,17 @@
     private final class H extends Handler {
         private static final int MSG_DISPATCH = 1;
         private static final int MSG_METRICS = 2;
+        private static final int MSG_SET_CONFIG = 3;
+
+        private final class ConfigMessageData {
+            public final ZenModeConfig config;
+            public final String reason;
+
+            ConfigMessageData(ZenModeConfig config, String reason) {
+                this.config = config;
+                this.reason = reason;
+            }
+        }
 
         private static final long METRICS_PERIOD_MS = 6 * 60 * 60 * 1000;
 
@@ -900,6 +919,10 @@
             sendEmptyMessageDelayed(MSG_METRICS, METRICS_PERIOD_MS);
         }
 
+        private void postSetConfig(ZenModeConfig config, String reason) {
+            sendMessage(obtainMessage(MSG_SET_CONFIG, new ConfigMessageData(config, reason)));
+        }
+
         @Override
         public void handleMessage(Message msg) {
             switch (msg.what) {
@@ -909,6 +932,10 @@
                 case MSG_METRICS:
                     mMetrics.emit();
                     break;
+                case MSG_SET_CONFIG:
+                    ConfigMessageData configData = (ConfigMessageData)msg.obj;
+                    setConfig(configData.config, configData.reason);
+                    break;
             }
         }
     }
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 0366fff..cf09b84 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -140,6 +140,7 @@
     private static final String ATTR_APP_ICON = "appIcon";
     private static final String ATTR_APP_LABEL = "appLabel";
     private static final String ATTR_ORIGINATING_URI = "originatingUri";
+    private static final String ATTR_ORIGINATING_UID = "originatingUid";
     private static final String ATTR_REFERRER_URI = "referrerUri";
     private static final String ATTR_ABI_OVERRIDE = "abiOverride";
     private static final String ATTR_VOLUME_UUID = "volumeUuid";
@@ -405,6 +406,8 @@
         params.appIcon = readBitmapAttribute(in, ATTR_APP_ICON);
         params.appLabel = readStringAttribute(in, ATTR_APP_LABEL);
         params.originatingUri = readUriAttribute(in, ATTR_ORIGINATING_URI);
+        params.originatingUid =
+                readIntAttribute(in, ATTR_ORIGINATING_UID, SessionParams.UID_UNKNOWN);
         params.referrerUri = readUriAttribute(in, ATTR_REFERRER_URI);
         params.abiOverride = readStringAttribute(in, ATTR_ABI_OVERRIDE);
         params.volumeUuid = readStringAttribute(in, ATTR_VOLUME_UUID);
@@ -477,6 +480,7 @@
         writeStringAttribute(out, ATTR_APP_PACKAGE_NAME, params.appPackageName);
         writeStringAttribute(out, ATTR_APP_LABEL, params.appLabel);
         writeUriAttribute(out, ATTR_ORIGINATING_URI, params.originatingUri);
+        writeIntAttribute(out, ATTR_ORIGINATING_UID, params.originatingUid);
         writeUriAttribute(out, ATTR_REFERRER_URI, params.referrerUri);
         writeStringAttribute(out, ATTR_ABI_OVERRIDE, params.abiOverride);
         writeStringAttribute(out, ATTR_VOLUME_UUID, params.volumeUuid);
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 4a473fd..6e32e5c 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -222,11 +222,17 @@
         // waived if the installer is the device owner.
         DevicePolicyManager dpm = (DevicePolicyManager) mContext.getSystemService(
                 Context.DEVICE_POLICY_SERVICE);
+        final boolean isPermissionGranted =
+                (mPm.checkUidPermission(android.Manifest.permission.INSTALL_PACKAGES, installerUid)
+                        == PackageManager.PERMISSION_GRANTED);
+        final boolean isInstallerRoot = (installerUid == Process.ROOT_UID);
+        final boolean forcePermissionPrompt =
+                (params.installFlags & PackageManager.INSTALL_FORCE_PERMISSION_PROMPT) != 0;
         mIsInstallerDeviceOwner = (dpm != null) && dpm.isDeviceOwnerApp(installerPackageName);
-        if ((mPm.checkUidPermission(android.Manifest.permission.INSTALL_PACKAGES, installerUid)
-                == PackageManager.PERMISSION_GRANTED)
-                || (installerUid == Process.ROOT_UID)
-                || mIsInstallerDeviceOwner) {
+        if ((isPermissionGranted
+                        || isInstallerRoot
+                        || mIsInstallerDeviceOwner)
+                && !forcePermissionPrompt) {
             mPermissionsAccepted = true;
         } else {
             mPermissionsAccepted = false;
@@ -940,7 +946,7 @@
         }
 
         final int uid = mPm.getPackageUid(PackageManagerService.DEFAULT_CONTAINER_PACKAGE,
-                UserHandle.USER_OWNER);
+                UserHandle.USER_SYSTEM);
         final int gid = UserHandle.getSharedAppGid(uid);
         if (!PackageHelper.fixSdPermissions(cid, gid, null)) {
             throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,
@@ -955,7 +961,9 @@
 
         if (accepted) {
             // Mark and kick off another install pass
-            mPermissionsAccepted = true;
+            synchronized (mLock) {
+                mPermissionsAccepted = true;
+            }
             mHandler.obtainMessage(MSG_COMMIT).sendToTarget();
         } else {
             destroyInternal();
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index c729e28..3e27c95 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -1108,7 +1108,7 @@
             Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT);
             Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
             if (mContext.bindServiceAsUser(service, mDefContainerConn,
-                    Context.BIND_AUTO_CREATE, UserHandle.OWNER)) {
+                    Context.BIND_AUTO_CREATE, UserHandle.SYSTEM)) {
                 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                 mBound = true;
                 return true;
@@ -1715,7 +1715,7 @@
                 for (PackageSetting ps : packages) {
                     Slog.d(TAG, "Destroying " + ps.name + " because volume was forgotten");
                     deletePackage(ps.name, new LegacyPackageDeleteObserver(null).getBinder(),
-                            UserHandle.USER_OWNER, PackageManager.DELETE_ALL_USERS);
+                            UserHandle.USER_SYSTEM, PackageManager.DELETE_ALL_USERS);
                 }
 
                 mSettings.onVolumeForgotten(fsUuid);
@@ -1726,7 +1726,7 @@
 
     private void grantRequestedRuntimePermissions(PackageParser.Package pkg, int userId,
             String[] grantedPermissions) {
-        if (userId >= UserHandle.USER_OWNER) {
+        if (userId >= UserHandle.USER_SYSTEM) {
             grantRequestedRuntimePermissionsForUser(pkg, userId, grantedPermissions);
         } else if (userId == UserHandle.USER_ALL) {
             final int[] userIds;
@@ -2276,7 +2276,7 @@
                         mSettings.enableSystemPackageLPw(packageName);
 
                         try {
-                            scanPackageTracedLI(scanFile, reparseFlags, scanFlags, 0, null);
+                            scanPackageTracedLI(scanFile, reparseFlags, scanFlags, 0, UserHandle.SYSTEM);
                         } catch (PackageManagerException e) {
                             Slog.e(TAG, "Failed to parse original system package: "
                                     + e.getMessage());
@@ -2401,8 +2401,9 @@
 
     private String getRequiredVerifierLPr() {
         final Intent verification = new Intent(Intent.ACTION_PACKAGE_NEEDS_VERIFICATION);
+        // We only care about verifier that's installed under system user.
         final List<ResolveInfo> receivers = queryIntentReceivers(verification, PACKAGE_MIME_TYPE,
-                PackageManager.GET_DISABLED_COMPONENTS, 0 /* TODO: Which userId? */);
+                PackageManager.GET_DISABLED_COMPONENTS, UserHandle.USER_SYSTEM);
 
         String requiredVerifier = null;
 
@@ -2417,7 +2418,7 @@
             final String packageName = info.activityInfo.packageName;
 
             if (checkPermission(android.Manifest.permission.PACKAGE_VERIFICATION_AGENT,
-                    packageName, UserHandle.USER_OWNER) != PackageManager.PERMISSION_GRANTED) {
+                    packageName, UserHandle.USER_SYSTEM) != PackageManager.PERMISSION_GRANTED) {
                 continue;
             }
 
@@ -2437,7 +2438,7 @@
         installerIntent.setDataAndType(Uri.fromFile(new File("foo.apk")), PACKAGE_MIME_TYPE);
 
         final List<ResolveInfo> installers = queryIntentActivities(installerIntent,
-                PACKAGE_MIME_TYPE, 0, 0);
+                PACKAGE_MIME_TYPE, 0, UserHandle.USER_SYSTEM);
 
         String requiredInstaller = null;
 
@@ -2467,7 +2468,7 @@
     private ComponentName getIntentFilterVerifierComponentNameLPr() {
         final Intent verification = new Intent(Intent.ACTION_INTENT_FILTER_NEEDS_VERIFICATION);
         final List<ResolveInfo> receivers = queryIntentReceivers(verification, PACKAGE_MIME_TYPE,
-                PackageManager.GET_DISABLED_COMPONENTS, 0 /* userId */);
+                PackageManager.GET_DISABLED_COMPONENTS, UserHandle.USER_SYSTEM);
 
         ComponentName verifierComponentName = null;
 
@@ -2488,7 +2489,7 @@
             }
 
             if (checkPermission(android.Manifest.permission.INTENT_FILTER_VERIFICATION_AGENT,
-                    packageName, UserHandle.USER_OWNER) != PackageManager.PERMISSION_GRANTED) {
+                    packageName, UserHandle.USER_SYSTEM) != PackageManager.PERMISSION_GRANTED) {
                 continue;
             }
 
@@ -2784,8 +2785,7 @@
             if((ps == null) || (ps.pkg == null) || (ps.pkg.applicationInfo == null)) {
                 return -1;
             }
-            p = ps.pkg;
-            return p != null ? UserHandle.getUid(userId, p.applicationInfo.uid) : -1;
+            return UserHandle.getUid(userId, ps.pkg.applicationInfo.uid);
         }
     }
 
@@ -5690,7 +5690,7 @@
             }
             try {
                 scanPackageTracedLI(file, parseFlags | PackageParser.PARSE_MUST_BE_APK,
-                        scanFlags, currentTime, null);
+                        scanFlags, currentTime, UserHandle.SYSTEM);
             } catch (PackageManagerException e) {
                 Slog.w(TAG, "Failed to parse " + file + ": " + e.getMessage());
 
@@ -5796,6 +5796,8 @@
      */
     private PackageParser.Package scanPackageLI(File scanFile, int parseFlags, int scanFlags,
             long currentTime, UserHandle user) throws PackageManagerException {
+        Preconditions.checkNotNull(user);
+
         if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanFile);
         parseFlags |= mDefParseFlags;
         PackageParser pp = new PackageParser();
@@ -5837,7 +5839,7 @@
         }
         boolean updatedPkgBetter = false;
         // First check if this is a system package that may involve an update
-        if (updatedPkg != null && (parseFlags&PackageParser.PARSE_IS_SYSTEM) != 0) {
+        if (updatedPkg != null && (parseFlags & PackageParser.PARSE_IS_SYSTEM) != 0) {
             // If new package is not located in "/system/priv-app" (e.g. due to an OTA),
             // it needs to drop FLAG_PRIVILEGED.
             if (locationIsPrivileged(scanFile)) {
@@ -6932,7 +6934,7 @@
         } else {
             // This is a normal package, need to make its data directory.
             dataPath = Environment.getDataUserPackageDirectory(pkg.volumeUuid,
-                    UserHandle.USER_OWNER, pkg.packageName);
+                    UserHandle.USER_SYSTEM, pkg.packageName);
 
             boolean uidError = false;
             if (dataPath.exists()) {
@@ -7090,7 +7092,7 @@
             // if they already exist
             if (!TextUtils.isEmpty(pkg.volumeUuid)) {
                 for (int userId : userIds) {
-                    if (userId != 0) {
+                    if (userId != UserHandle.USER_SYSTEM) {
                         mInstaller.createUserData(pkg.volumeUuid, pkg.packageName,
                                 UserHandle.getUid(userId, pkg.applicationInfo.uid), userId,
                                 pkg.applicationInfo.seinfo);
@@ -7218,7 +7220,6 @@
                                 for (int j=0; j<sysPs.pkg.libraryNames.size(); j++) {
                                     if (name.equals(sysPs.pkg.libraryNames.get(j))) {
                                         allowed = true;
-                                        allowed = true;
                                         break;
                                     }
                                 }
@@ -9603,7 +9604,7 @@
         if (am != null) {
             try {
                 am.startService(null, intent, null, mContext.getOpPackageName(),
-                        UserHandle.USER_OWNER);
+                        UserHandle.USER_SYSTEM);
             } catch (RemoteException e) {
             }
         }
@@ -9686,7 +9687,8 @@
             IPackageInstallObserver2 observer, PackageInstaller.SessionParams sessionParams,
             String installerPackageName, int installerUid, UserHandle user) {
         final VerificationParams verifParams = new VerificationParams(
-                null, sessionParams.originatingUri, sessionParams.referrerUri, installerUid, null);
+                null, sessionParams.originatingUri, sessionParams.referrerUri,
+                sessionParams.originatingUid, null);
         verifParams.setInstallerUid(installerUid);
 
         final OriginInfo origin;
@@ -10321,7 +10323,8 @@
                                 + " to BM for possible restore");
                         Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "restore", token);
                         try {
-                            if (bm.isBackupServiceActive(UserHandle.USER_OWNER)) {
+                            // TODO: http://b/22388012
+                            if (bm.isBackupServiceActive(UserHandle.USER_SYSTEM)) {
                                 bm.restoreAtInstall(res.pkg.applicationInfo.packageName, token);
                             } else {
                                 doRestore = false;
@@ -10802,14 +10805,11 @@
             mArgs = args;
 
             if (ret == PackageManager.INSTALL_SUCCEEDED) {
-                 /*
-                 * ADB installs appear as UserHandle.USER_ALL, and can only be performed by
-                 * UserHandle.USER_OWNER, so use the package verifier for UserHandle.USER_OWNER.
-                 */
-                int userIdentifier = getUser().getIdentifier();
-                if (userIdentifier == UserHandle.USER_ALL
-                        && ((installFlags & PackageManager.INSTALL_FROM_ADB) != 0)) {
-                    userIdentifier = UserHandle.USER_OWNER;
+                // TODO: http://b/22976637
+                // Apps installed for "all" users use the device owner to verify the app
+                UserHandle verifierUser = getUser();
+                if (verifierUser == UserHandle.ALL) {
+                    verifierUser = UserHandle.SYSTEM;
                 }
 
                 /*
@@ -10817,9 +10817,9 @@
                  * do, then we'll defer to them to verify the packages.
                  */
                 final int requiredUid = mRequiredVerifierPackage == null ? -1
-                        : getPackageUid(mRequiredVerifierPackage, userIdentifier);
+                        : getPackageUid(mRequiredVerifierPackage, verifierUser.getIdentifier());
                 if (!origin.existing && requiredUid != -1
-                        && isVerificationEnabled(userIdentifier, installFlags)) {
+                        && isVerificationEnabled(verifierUser.getIdentifier(), installFlags)) {
                     final Intent verification = new Intent(
                             Intent.ACTION_PACKAGE_NEEDS_VERIFICATION);
                     verification.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
@@ -10829,7 +10829,7 @@
 
                     final List<ResolveInfo> receivers = queryIntentReceivers(verification,
                             PACKAGE_MIME_TYPE, PackageManager.GET_DISABLED_COMPONENTS,
-                            0 /* TODO: Which userId? */);
+                            verifierUser.getIdentifier());
 
                     if (DEBUG_VERIFY) {
                         Slog.d(TAG, "Found " + receivers.size() + " verifiers for intent "
@@ -10884,12 +10884,6 @@
                     final List<ComponentName> sufficientVerifiers = matchVerifiers(pkgLite,
                             receivers, verificationState);
 
-                    // Apps installed for "all" users use the device owner to verify the app
-                    UserHandle verifierUser = getUser();
-                    if (verifierUser == UserHandle.ALL) {
-                        verifierUser = UserHandle.OWNER;
-                    }
-
                     /*
                      * If any sufficient verifiers were listed in the package
                      * manifest, attempt to ask them.
@@ -12120,7 +12114,8 @@
                         (oldExternal ? PackageParser.PARSE_EXTERNAL_STORAGE : 0);
                 int oldScanFlags = SCAN_UPDATE_SIGNATURE | SCAN_UPDATE_TIME;
                 try {
-                    scanPackageTracedLI(restoreFile, oldParseFlags, oldScanFlags, origUpdateTime, null);
+                    scanPackageTracedLI(restoreFile, oldParseFlags, oldScanFlags, origUpdateTime,
+                            UserHandle.SYSTEM);
                 } catch (PackageManagerException e) {
                     Slog.e(TAG, "Failed to restore package : " + pkgName + " after failed upgrade: "
                             + e.getMessage());
@@ -12615,7 +12610,7 @@
 
         final int verifierUid = getPackageUid(
                 mIntentFilterVerifierComponent.getPackageName(),
-                (userId == UserHandle.USER_ALL) ? UserHandle.USER_OWNER : userId);
+                (userId == UserHandle.USER_ALL) ? UserHandle.USER_SYSTEM : userId);
 
         mHandler.removeMessages(START_INTENT_FILTER_VERIFICATIONS);
         final Message msg = mHandler.obtainMessage(START_INTENT_FILTER_VERIFICATIONS);
@@ -12815,11 +12810,14 @@
         Preconditions.checkNotNull(packageName);
         Preconditions.checkNotNull(observer);
         final int uid = Binder.getCallingUid();
-        if (UserHandle.getUserId(uid) != userId) {
+        final boolean deleteAllUsers = (flags & PackageManager.DELETE_ALL_USERS) != 0;
+        final int[] users = deleteAllUsers ? sUserManager.getUserIds() : new int[]{ userId };
+        if (UserHandle.getUserId(uid) != userId || (deleteAllUsers && users.length > 1)) {
             mContext.enforceCallingPermission(
                     android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
                     "deletePackage for user " + userId);
         }
+
         if (isUserRestricted(userId, UserManager.DISALLOW_UNINSTALL_APPS)) {
             try {
                 observer.onPackageDeleted(packageName,
@@ -12829,25 +12827,15 @@
             return;
         }
 
-        boolean uninstallBlocked = false;
-        if ((flags & PackageManager.DELETE_ALL_USERS) != 0) {
-            int[] users = sUserManager.getUserIds();
-            for (int i = 0; i < users.length; ++i) {
-                if (getBlockUninstallForUser(packageName, users[i])) {
-                    uninstallBlocked = true;
-                    break;
+        for (int currentUserId : users) {
+            if (getBlockUninstallForUser(packageName, currentUserId)) {
+                try {
+                    observer.onPackageDeleted(packageName,
+                            PackageManager.DELETE_FAILED_OWNER_BLOCKED, null);
+                } catch (RemoteException re) {
                 }
+                return;
             }
-        } else {
-            uninstallBlocked = getBlockUninstallForUser(packageName, userId);
-        }
-        if (uninstallBlocked) {
-            try {
-                observer.onPackageDeleted(packageName, PackageManager.DELETE_FAILED_OWNER_BLOCKED,
-                        null);
-            } catch (RemoteException re) {
-            }
-            return;
         }
 
         if (DEBUG_REMOVE) {
@@ -12858,13 +12846,11 @@
             public void run() {
                 mHandler.removeCallbacks(this);
                 final int returnCode = deletePackageX(packageName, userId, flags);
-                if (observer != null) {
-                    try {
-                        observer.onPackageDeleted(packageName, returnCode, null);
-                    } catch (RemoteException e) {
-                        Log.i(TAG, "Observer no longer exists.");
-                    } //end catch
-                } //end if
+                try {
+                    observer.onPackageDeleted(packageName, returnCode, null);
+                } catch (RemoteException e) {
+                    Log.i(TAG, "Observer no longer exists.");
+                } //end catch
             } //end run
         });
     }
@@ -12874,9 +12860,11 @@
                 ServiceManager.getService(Context.DEVICE_POLICY_SERVICE));
         try {
             if (dpm != null) {
-                if (dpm.isDeviceOwner(packageName)) {
+                // Does the package contains the device owner?
+                if (dpm.isDeviceOwnerPackage(packageName)) {
                     return true;
                 }
+                // Does it contain a device admin for any user?
                 int[] users;
                 if (userId == UserHandle.USER_ALL) {
                     users = sUserManager.getUserIds();
@@ -13062,7 +13050,7 @@
                             final int userIdToKill = mSettings.updateSharedUserPermsLPw(deletedPs,
                                     userId);
                             if (userIdToKill == UserHandle.USER_ALL
-                                    || userIdToKill >= UserHandle.USER_OWNER) {
+                                    || userIdToKill >= UserHandle.USER_SYSTEM) {
                                 // If gids changed for this user, kill all affected packages.
                                 mHandler.post(new Runnable() {
                                     @Override
@@ -13179,7 +13167,8 @@
 
         final PackageParser.Package newPkg;
         try {
-            newPkg = scanPackageTracedLI(disabledPs.codePath, parseFlags, SCAN_NO_PATHS, 0, null);
+            newPkg = scanPackageTracedLI(disabledPs.codePath, parseFlags, SCAN_NO_PATHS, 0,
+                    UserHandle.SYSTEM);
         } catch (PackageManagerException e) {
             Slog.w(TAG, "Failed to restore system package:" + newPs.name + ": " + e.getMessage());
             return false;
@@ -13430,7 +13419,7 @@
         }
         final ClearStorageConnection conn = new ClearStorageConnection();
         if (mContext.bindServiceAsUser(
-                containerIntent, conn, Context.BIND_AUTO_CREATE, UserHandle.OWNER)) {
+                containerIntent, conn, Context.BIND_AUTO_CREATE, UserHandle.SYSTEM)) {
             try {
                 for (int curUser : users) {
                     long timeout = SystemClock.uptimeMillis() + 5000;
@@ -15742,7 +15731,8 @@
                 synchronized (mInstallLock) {
                     PackageParser.Package pkg = null;
                     try {
-                        pkg = scanPackageTracedLI(new File(codePath), parseFlags, 0, 0, null);
+                        pkg = scanPackageTracedLI(new File(codePath), parseFlags, 0, 0,
+                                UserHandle.SYSTEM);
                     } catch (PackageManagerException e) {
                         Slog.w(TAG, "Failed to scan " + codePath + ": " + e.getMessage());
                     }
@@ -15902,7 +15892,8 @@
             synchronized (mInstallLock) {
                 final PackageParser.Package pkg;
                 try {
-                    pkg = scanPackageTracedLI(ps.codePath, parseFlags, SCAN_INITIAL, 0L, null);
+                    pkg = scanPackageTracedLI(ps.codePath, parseFlags, SCAN_INITIAL, 0,
+                            UserHandle.SYSTEM);
                     loaded.add(pkg.applicationInfo);
                 } catch (PackageManagerException e) {
                     Slog.w(TAG, "Failed to scan " + ps.codePath + ": " + e.getMessage());
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index de106a1..341410d 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -20,6 +20,7 @@
 import android.annotation.NonNull;
 import android.app.Activity;
 import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
 import android.app.ActivityManagerNative;
 import android.app.IStopUserCallback;
 import android.app.admin.DevicePolicyManager;
@@ -58,13 +59,11 @@
 import android.util.TimeUtils;
 import android.util.Xml;
 
-import com.google.android.collect.Sets;
-
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.IAppOpsService;
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.XmlUtils;
-import com.android.server.accounts.AccountManagerService;
+import com.android.server.LocalServices;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -81,7 +80,6 @@
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.List;
-import java.util.Set;
 
 import libcore.io.IoUtils;
 
@@ -130,6 +128,8 @@
     private static final String XML_SUFFIX = ".xml";
 
     private static final int MIN_USER_ID = 10;
+    // We need to keep process uid within Integer.MAX_VALUE.
+    private static final int MAX_USER_ID = Integer.MAX_VALUE / UserHandle.PER_USER_RANGE;
 
     private static final int USER_VERSION = 6;
 
@@ -144,10 +144,6 @@
      */
     private static final boolean CONFIG_PROFILES_SHARE_CREDENTIAL = true;
 
-    // Set of user restrictions, which can only be enforced by the system
-    private static final Set<String> SYSTEM_CONTROLLED_RESTRICTIONS = Sets.newArraySet(
-            UserManager.DISALLOW_RECORD_AUDIO);
-
     static final int WRITE_USER_MSG = 1;
     static final int WRITE_USER_DELAY = 2*1000;  // 2 seconds
 
@@ -242,13 +238,15 @@
             synchronized (mPackagesLock) {
                 // Prune out any partially created/partially removed users.
                 ArrayList<UserInfo> partials = new ArrayList<UserInfo>();
-                for (int i = 0; i < mUsers.size(); i++) {
+                final int userSize = mUsers.size();
+                for (int i = 0; i < userSize; i++) {
                     UserInfo ui = mUsers.valueAt(i);
                     if ((ui.partial || ui.guestToRemove) && i != 0) {
                         partials.add(ui);
                     }
                 }
-                for (int i = 0; i < partials.size(); i++) {
+                final int partialsSize = partials.size();
+                for (int i = 0; i < partialsSize; i++) {
                     UserInfo ui = partials.get(i);
                     Slog.w(LOG_TAG, "Removing partially created user " + ui.id
                             + " (name=" + ui.name + ")");
@@ -272,7 +270,8 @@
     public UserInfo getPrimaryUser() {
         checkManageUsersPermission("query users");
         synchronized (mPackagesLock) {
-            for (int i = 0; i < mUsers.size(); i++) {
+            final int userSize = mUsers.size();
+            for (int i = 0; i < userSize; i++) {
                 UserInfo ui = mUsers.valueAt(i);
                 if (ui.isPrimary()) {
                     return ui;
@@ -287,7 +286,8 @@
         checkManageUsersPermission("query users");
         synchronized (mPackagesLock) {
             ArrayList<UserInfo> users = new ArrayList<UserInfo>(mUsers.size());
-            for (int i = 0; i < mUsers.size(); i++) {
+            final int userSize = mUsers.size();
+            for (int i = 0; i < userSize; i++) {
                 UserInfo ui = mUsers.valueAt(i);
                 if (ui.partial) {
                     continue;
@@ -323,7 +323,8 @@
             // Probably a dying user
             return users;
         }
-        for (int i = 0; i < mUsers.size(); i++) {
+        final int userSize = mUsers.size();
+        for (int i = 0; i < userSize; i++) {
             UserInfo profile = mUsers.valueAt(i);
             if (!isProfileOf(user, profile)) {
                 continue;
@@ -588,7 +589,7 @@
     public void setUserRestriction(String key, boolean value, int userId) {
         checkManageUsersPermission("setUserRestriction");
         synchronized (mPackagesLock) {
-            if (!SYSTEM_CONTROLLED_RESTRICTIONS.contains(key)) {
+            if (!UserRestrictionsUtils.SYSTEM_CONTROLLED_USER_RESTRICTIONS.contains(key)) {
                 Bundle restrictions = getUserRestrictions(userId);
                 restrictions.putBoolean(key, value);
                 setUserRestrictionsInternalLocked(restrictions, userId);
@@ -614,7 +615,7 @@
         synchronized (mPackagesLock) {
             final Bundle oldUserRestrictions = mUserRestrictions.get(userId);
             // Restore the original state of system controlled restrictions from oldUserRestrictions
-            for (String key : SYSTEM_CONTROLLED_RESTRICTIONS) {
+            for (String key : UserRestrictionsUtils.SYSTEM_CONTROLLED_USER_RESTRICTIONS) {
                 restrictions.remove(key);
                 if (oldUserRestrictions.containsKey(key)) {
                     restrictions.putBoolean(key, oldUserRestrictions.getBoolean(key));
@@ -807,7 +808,8 @@
                                 && type != XmlPullParser.END_TAG) {
                             if (type == XmlPullParser.START_TAG) {
                                 if (parser.getName().equals(TAG_RESTRICTIONS)) {
-                                    readRestrictionsLocked(parser, mGuestRestrictions);
+                                    UserRestrictionsUtils
+                                            .readRestrictions(parser, mGuestRestrictions);
                                 }
                                 break;
                             }
@@ -838,7 +840,7 @@
         int userVersion = mUserVersion;
         if (userVersion < 1) {
             // Assign a proper name for the owner, if not initialized correctly before
-            UserInfo user = mUsers.get(UserHandle.USER_OWNER);
+            UserInfo user = mUsers.get(UserHandle.USER_SYSTEM);
             if ("Primary".equals(user.name)) {
                 user.name = mContext.getResources().getString(com.android.internal.R.string.owner_name);
                 scheduleWriteUserLocked(user);
@@ -848,7 +850,7 @@
 
         if (userVersion < 2) {
             // Owner should be marked as initialized
-            UserInfo user = mUsers.get(UserHandle.USER_OWNER);
+            UserInfo user = mUsers.get(UserHandle.USER_SYSTEM);
             if ((user.flags & UserInfo.FLAG_INITIALIZED) == 0) {
                 user.flags |= UserInfo.FLAG_INITIALIZED;
                 scheduleWriteUserLocked(user);
@@ -970,7 +972,7 @@
             serializer.endTag(null, TAG_NAME);
             Bundle restrictions = mUserRestrictions.get(userInfo.id);
             if (restrictions != null) {
-                writeRestrictionsLocked(serializer, restrictions);
+                UserRestrictionsUtils.writeRestrictions(serializer, restrictions, TAG_RESTRICTIONS);
             }
             serializer.endTag(null, TAG_USER);
 
@@ -1008,9 +1010,11 @@
             serializer.attribute(null, ATTR_USER_VERSION, Integer.toString(mUserVersion));
 
             serializer.startTag(null, TAG_GUEST_RESTRICTIONS);
-            writeRestrictionsLocked(serializer, mGuestRestrictions);
+            UserRestrictionsUtils
+                    .writeRestrictions(serializer, mGuestRestrictions, TAG_RESTRICTIONS);
             serializer.endTag(null, TAG_GUEST_RESTRICTIONS);
-            for (int i = 0; i < mUsers.size(); i++) {
+            final int userSize = mUsers.size();
+            for (int i = 0; i < userSize; i++) {
                 UserInfo user = mUsers.valueAt(i);
                 serializer.startTag(null, TAG_USER);
                 serializer.attribute(null, ATTR_ID, Integer.toString(user.id));
@@ -1027,45 +1031,6 @@
         }
     }
 
-    private void writeRestrictionsLocked(XmlSerializer serializer, Bundle restrictions)
-            throws IOException {
-        serializer.startTag(null, TAG_RESTRICTIONS);
-        writeBoolean(serializer, restrictions, UserManager.DISALLOW_CONFIG_WIFI);
-        writeBoolean(serializer, restrictions, UserManager.DISALLOW_MODIFY_ACCOUNTS);
-        writeBoolean(serializer, restrictions, UserManager.DISALLOW_INSTALL_APPS);
-        writeBoolean(serializer, restrictions, UserManager.DISALLOW_UNINSTALL_APPS);
-        writeBoolean(serializer, restrictions, UserManager.DISALLOW_SHARE_LOCATION);
-        writeBoolean(serializer, restrictions,
-                UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES);
-        writeBoolean(serializer, restrictions, UserManager.DISALLOW_CONFIG_BLUETOOTH);
-        writeBoolean(serializer, restrictions, UserManager.DISALLOW_USB_FILE_TRANSFER);
-        writeBoolean(serializer, restrictions, UserManager.DISALLOW_CONFIG_CREDENTIALS);
-        writeBoolean(serializer, restrictions, UserManager.DISALLOW_REMOVE_USER);
-        writeBoolean(serializer, restrictions, UserManager.DISALLOW_DEBUGGING_FEATURES);
-        writeBoolean(serializer, restrictions, UserManager.DISALLOW_CONFIG_VPN);
-        writeBoolean(serializer, restrictions, UserManager.DISALLOW_CONFIG_TETHERING);
-        writeBoolean(serializer, restrictions, UserManager.DISALLOW_NETWORK_RESET);
-        writeBoolean(serializer, restrictions, UserManager.DISALLOW_FACTORY_RESET);
-        writeBoolean(serializer, restrictions, UserManager.DISALLOW_ADD_USER);
-        writeBoolean(serializer, restrictions, UserManager.ENSURE_VERIFY_APPS);
-        writeBoolean(serializer, restrictions, UserManager.DISALLOW_CONFIG_CELL_BROADCASTS);
-        writeBoolean(serializer, restrictions, UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS);
-        writeBoolean(serializer, restrictions, UserManager.DISALLOW_APPS_CONTROL);
-        writeBoolean(serializer, restrictions, UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA);
-        writeBoolean(serializer, restrictions, UserManager.DISALLOW_UNMUTE_MICROPHONE);
-        writeBoolean(serializer, restrictions, UserManager.DISALLOW_ADJUST_VOLUME);
-        writeBoolean(serializer, restrictions, UserManager.DISALLOW_OUTGOING_CALLS);
-        writeBoolean(serializer, restrictions, UserManager.DISALLOW_SMS);
-        writeBoolean(serializer, restrictions, UserManager.DISALLOW_FUN);
-        writeBoolean(serializer, restrictions, UserManager.DISALLOW_CREATE_WINDOWS);
-        writeBoolean(serializer, restrictions, UserManager.DISALLOW_CROSS_PROFILE_COPY_PASTE);
-        writeBoolean(serializer, restrictions, UserManager.DISALLOW_OUTGOING_BEAM);
-        writeBoolean(serializer, restrictions, UserManager.DISALLOW_WALLPAPER);
-        writeBoolean(serializer, restrictions, UserManager.DISALLOW_SAFE_BOOT);
-        writeBoolean(serializer, restrictions, UserManager.ALLOW_PARENT_PROFILE_APP_LINKING);
-        serializer.endTag(null, TAG_RESTRICTIONS);
-    }
-
     private UserInfo readUserLocked(int id) {
         int flags = 0;
         int serialNumber = id;
@@ -1134,7 +1099,7 @@
                             name = parser.getText();
                         }
                     } else if (TAG_RESTRICTIONS.equals(tag)) {
-                        readRestrictionsLocked(parser, restrictions);
+                        UserRestrictionsUtils.readRestrictions(parser, restrictions);
                     }
                 }
             }
@@ -1163,60 +1128,6 @@
         return null;
     }
 
-    private void readRestrictionsLocked(XmlPullParser parser, Bundle restrictions)
-            throws IOException {
-        readBoolean(parser, restrictions, UserManager.DISALLOW_CONFIG_WIFI);
-        readBoolean(parser, restrictions, UserManager.DISALLOW_MODIFY_ACCOUNTS);
-        readBoolean(parser, restrictions, UserManager.DISALLOW_INSTALL_APPS);
-        readBoolean(parser, restrictions, UserManager.DISALLOW_UNINSTALL_APPS);
-        readBoolean(parser, restrictions, UserManager.DISALLOW_SHARE_LOCATION);
-        readBoolean(parser, restrictions,
-                UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES);
-        readBoolean(parser, restrictions, UserManager.DISALLOW_CONFIG_BLUETOOTH);
-        readBoolean(parser, restrictions, UserManager.DISALLOW_USB_FILE_TRANSFER);
-        readBoolean(parser, restrictions, UserManager.DISALLOW_CONFIG_CREDENTIALS);
-        readBoolean(parser, restrictions, UserManager.DISALLOW_REMOVE_USER);
-        readBoolean(parser, restrictions, UserManager.DISALLOW_DEBUGGING_FEATURES);
-        readBoolean(parser, restrictions, UserManager.DISALLOW_CONFIG_VPN);
-        readBoolean(parser, restrictions, UserManager.DISALLOW_CONFIG_TETHERING);
-        readBoolean(parser, restrictions, UserManager.DISALLOW_NETWORK_RESET);
-        readBoolean(parser, restrictions, UserManager.DISALLOW_FACTORY_RESET);
-        readBoolean(parser, restrictions, UserManager.DISALLOW_ADD_USER);
-        readBoolean(parser, restrictions, UserManager.ENSURE_VERIFY_APPS);
-        readBoolean(parser, restrictions, UserManager.DISALLOW_CONFIG_CELL_BROADCASTS);
-        readBoolean(parser, restrictions, UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS);
-        readBoolean(parser, restrictions, UserManager.DISALLOW_APPS_CONTROL);
-        readBoolean(parser, restrictions,
-                UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA);
-        readBoolean(parser, restrictions, UserManager.DISALLOW_UNMUTE_MICROPHONE);
-        readBoolean(parser, restrictions, UserManager.DISALLOW_ADJUST_VOLUME);
-        readBoolean(parser, restrictions, UserManager.DISALLOW_OUTGOING_CALLS);
-        readBoolean(parser, restrictions, UserManager.DISALLOW_SMS);
-        readBoolean(parser, restrictions, UserManager.DISALLOW_FUN);
-        readBoolean(parser, restrictions, UserManager.DISALLOW_CREATE_WINDOWS);
-        readBoolean(parser, restrictions, UserManager.DISALLOW_CROSS_PROFILE_COPY_PASTE);
-        readBoolean(parser, restrictions, UserManager.DISALLOW_OUTGOING_BEAM);
-        readBoolean(parser, restrictions, UserManager.DISALLOW_WALLPAPER);
-        readBoolean(parser, restrictions, UserManager.DISALLOW_SAFE_BOOT);
-        readBoolean(parser, restrictions, UserManager.ALLOW_PARENT_PROFILE_APP_LINKING);
-    }
-
-    private void readBoolean(XmlPullParser parser, Bundle restrictions,
-            String restrictionKey) {
-        String value = parser.getAttributeValue(null, restrictionKey);
-        if (value != null) {
-            restrictions.putBoolean(restrictionKey, Boolean.parseBoolean(value));
-        }
-    }
-
-    private void writeBoolean(XmlSerializer xml, Bundle restrictions, String restrictionKey)
-            throws IOException {
-        if (restrictions.containsKey(restrictionKey)) {
-            xml.attribute(null, restrictionKey,
-                    Boolean.toString(restrictions.getBoolean(restrictionKey)));
-        }
-    }
-
     private int readIntAttribute(XmlPullParser parser, String attr, int defaultValue) {
         String valueString = parser.getAttributeValue(null, attr);
         if (valueString == null) return defaultValue;
@@ -1511,6 +1422,11 @@
         long ident = Binder.clearCallingIdentity();
         try {
             final UserInfo user;
+            int currentUser = ActivityManager.getCurrentUser();
+            if (currentUser == userHandle) {
+                Log.w(LOG_TAG, "Current user cannot be removed");
+                return false;
+            }
             synchronized (mPackagesLock) {
                 user = mUsers.get(userHandle);
                 if (userHandle == 0 || user == null || mRemovingUserIds.get(userHandle)) {
@@ -1587,6 +1503,9 @@
                             }
                             new Thread() {
                                 public void run() {
+                                    // Clean up any ActivityManager state
+                                    LocalServices.getService(ActivityManagerInternal.class)
+                                            .onUserRemoved(userHandle);
                                     synchronized (mInstallLock) {
                                         synchronized (mPackagesLock) {
                                             removeUserStateLocked(userHandle);
@@ -1951,14 +1870,15 @@
      */
     private void updateUserIdsLocked() {
         int num = 0;
-        for (int i = 0; i < mUsers.size(); i++) {
+        final int userSize = mUsers.size();
+        for (int i = 0; i < userSize; i++) {
             if (!mUsers.valueAt(i).partial) {
                 num++;
             }
         }
         final int[] newUsers = new int[num];
         int n = 0;
-        for (int i = 0; i < mUsers.size(); i++) {
+        for (int i = 0; i < userSize; i++) {
             if (!mUsers.valueAt(i).partial) {
                 newUsers[n++] = mUsers.keyAt(i);
             }
@@ -1994,14 +1914,14 @@
     private int getNextAvailableIdLocked() {
         synchronized (mPackagesLock) {
             int i = MIN_USER_ID;
-            while (i < Integer.MAX_VALUE) {
+            while (i < MAX_USER_ID) {
                 if (mUsers.indexOfKey(i) < 0 && !mRemovingUserIds.get(i)) {
-                    break;
+                    return i;
                 }
                 i++;
             }
-            return i;
         }
+        throw new IllegalStateException("No user id available!");
     }
 
     private String packageToRestrictionsFileName(String packageName) {
@@ -2124,7 +2044,13 @@
                     sb.append(" ago");
                     pw.println(sb);
                 }
+                pw.println("    Restrictions:");
+                UserRestrictionsUtils.dumpRestrictions(
+                        pw, "      ", mUserRestrictions.get(user.id));
             }
+            pw.println();
+            pw.println("Guest restrictions:");
+            UserRestrictionsUtils.dumpRestrictions(pw, "  ", mGuestRestrictions);
         }
     }
 
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
new file mode 100644
index 0000000..db1fd2e
--- /dev/null
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import com.google.android.collect.Sets;
+
+import com.android.internal.util.Preconditions;
+
+import android.os.Bundle;
+import android.os.UserManager;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Set;
+
+public class UserRestrictionsUtils {
+    private UserRestrictionsUtils() {
+    }
+
+    public static final String[] USER_RESTRICTIONS = {
+            UserManager.DISALLOW_CONFIG_WIFI,
+            UserManager.DISALLOW_MODIFY_ACCOUNTS,
+            UserManager.DISALLOW_INSTALL_APPS,
+            UserManager.DISALLOW_UNINSTALL_APPS,
+            UserManager.DISALLOW_SHARE_LOCATION,
+            UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES,
+            UserManager.DISALLOW_CONFIG_BLUETOOTH,
+            UserManager.DISALLOW_USB_FILE_TRANSFER,
+            UserManager.DISALLOW_CONFIG_CREDENTIALS,
+            UserManager.DISALLOW_REMOVE_USER,
+            UserManager.DISALLOW_DEBUGGING_FEATURES,
+            UserManager.DISALLOW_CONFIG_VPN,
+            UserManager.DISALLOW_CONFIG_TETHERING,
+            UserManager.DISALLOW_NETWORK_RESET,
+            UserManager.DISALLOW_FACTORY_RESET,
+            UserManager.DISALLOW_ADD_USER,
+            UserManager.ENSURE_VERIFY_APPS,
+            UserManager.DISALLOW_CONFIG_CELL_BROADCASTS,
+            UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS,
+            UserManager.DISALLOW_APPS_CONTROL,
+            UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA,
+            UserManager.DISALLOW_UNMUTE_MICROPHONE,
+            UserManager.DISALLOW_ADJUST_VOLUME,
+            UserManager.DISALLOW_OUTGOING_CALLS,
+            UserManager.DISALLOW_SMS,
+            UserManager.DISALLOW_FUN,
+            UserManager.DISALLOW_CREATE_WINDOWS,
+            UserManager.DISALLOW_CROSS_PROFILE_COPY_PASTE,
+            UserManager.DISALLOW_OUTGOING_BEAM,
+            UserManager.DISALLOW_WALLPAPER,
+            UserManager.DISALLOW_SAFE_BOOT,
+            UserManager.ALLOW_PARENT_PROFILE_APP_LINKING,
+            UserManager.DISALLOW_RECORD_AUDIO,
+    };
+
+    /**
+     * Set of user restrictions, which can only be enforced by the system.
+     */
+    public static final Set<String> SYSTEM_CONTROLLED_USER_RESTRICTIONS = Sets.newArraySet(
+            UserManager.DISALLOW_RECORD_AUDIO);
+
+    /**
+     * Set of user restriction which we don't want to persist.
+     */
+    public static final Set<String> NON_PERSIST_USER_RESTRICTIONS = Sets.newArraySet(
+            UserManager.DISALLOW_RECORD_AUDIO);
+
+    public static void writeRestrictions(XmlSerializer serializer, Bundle restrictions,
+            String tag) throws IOException {
+        serializer.startTag(null, tag);
+        for (String key : USER_RESTRICTIONS) {
+            //
+            if (restrictions.getBoolean(key)
+                    && !NON_PERSIST_USER_RESTRICTIONS.contains(key)) {
+                serializer.attribute(null, key, "true");
+            }
+        }
+        serializer.endTag(null, tag);
+    }
+
+    public static void readRestrictions(XmlPullParser parser, Bundle restrictions)
+            throws IOException {
+        for (String key : USER_RESTRICTIONS) {
+            final String value = parser.getAttributeValue(null, key);
+            if (value != null) {
+                restrictions.putBoolean(key, Boolean.parseBoolean(value));
+            }
+        }
+    }
+
+    public static void dumpRestrictions(PrintWriter pw, String prefix, Bundle restrictions) {
+        boolean noneSet = true;
+        if (restrictions != null) {
+            for (String key : restrictions.keySet()) {
+                if (restrictions.getBoolean(key, false)) {
+                    pw.println(prefix + key);
+                    noneSet = false;
+                }
+            }
+        }
+        if (noneSet) {
+            pw.println(prefix + "none");
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index c265000..16add37 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -16,6 +16,16 @@
 
 package com.android.server.policy;
 
+import static android.app.ActivityManager.DOCKED_STACK_ID;
+import static android.app.ActivityManager.FREEFORM_WORKSPACE_STACK_ID;
+import static android.view.WindowManager.LayoutParams.*;
+import static android.view.WindowManagerPolicy.WindowManagerFuncs.LID_ABSENT;
+import static android.view.WindowManagerPolicy.WindowManagerFuncs.LID_OPEN;
+import static android.view.WindowManagerPolicy.WindowManagerFuncs.LID_CLOSED;
+import static android.view.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVER_ABSENT;
+import static android.view.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_UNCOVERED;
+import static android.view.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVERED;
+
 import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
 import android.app.ActivityManagerInternal.SleepToken;
@@ -40,7 +50,6 @@
 import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
 import android.content.res.Resources;
-import android.content.res.TypedArray;
 import android.database.ContentObserver;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
@@ -130,14 +139,6 @@
 import java.util.HashSet;
 import java.util.List;
 
-import static android.view.WindowManager.LayoutParams.*;
-import static android.view.WindowManagerPolicy.WindowManagerFuncs.LID_ABSENT;
-import static android.view.WindowManagerPolicy.WindowManagerFuncs.LID_OPEN;
-import static android.view.WindowManagerPolicy.WindowManagerFuncs.LID_CLOSED;
-import static android.view.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVER_ABSENT;
-import static android.view.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_UNCOVERED;
-import static android.view.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVERED;
-
 /**
  * WindowManagerPolicy implementation for the Android phone UI.  This
  * introduces a new method suffix, Lp, for an internal lock of the
@@ -778,6 +779,8 @@
         private final Runnable mUpdateRotationRunnable = new Runnable() {
             @Override
             public void run() {
+                // send interaction hint to improve redraw performance
+                mPowerManagerInternal.powerHint(PowerManagerInternal.POWER_HINT_INTERACTION, 0);
                 updateRotation(false);
             }
         };
@@ -4625,7 +4628,9 @@
                     if (mStatusBarController.setBarShowingLw(true)) {
                         changes |= FINISH_LAYOUT_REDO_LAYOUT;
                     }
-                } else if (topIsFullscreen) {
+                } else if (topIsFullscreen
+                        && !mWindowManagerInternal.isStackVisible(FREEFORM_WORKSPACE_STACK_ID)
+                        && !mWindowManagerInternal.isStackVisible(DOCKED_STACK_ID)) {
                     if (DEBUG_LAYOUT) Slog.v(TAG, "** HIDING status bar");
                     if (mStatusBarController.setBarShowingLw(false)) {
                         changes |= FINISH_LAYOUT_REDO_LAYOUT;
@@ -6712,6 +6717,15 @@
     }
 
     private int updateSystemBarsLw(WindowState win, int oldVis, int vis) {
+        final boolean dockedStackVisible = mWindowManagerInternal.isStackVisible(DOCKED_STACK_ID);
+        final boolean freeformStackVisible =
+                mWindowManagerInternal.isStackVisible(FREEFORM_WORKSPACE_STACK_ID);
+        final boolean forceShowSystemBars = dockedStackVisible || freeformStackVisible;
+        // TODO(multi-window): Update to force opaque independently for status bar and nav bar.
+        // This will require refactoring the code to have separate vis flag for each bar so it can
+        // be adjusted independently.
+        final boolean forceOpaqueSystemBars = forceShowSystemBars;
+
         // apply translucent bar vis flags
         WindowState transWin = isStatusBarKeyguard() && !mHideLockScreen
                 ? mStatusBar
@@ -6734,7 +6748,8 @@
             vis = (vis & ~flags) | (oldVis & flags);
         }
 
-        if (!areTranslucentBarsAllowed() && transWin != mStatusBar) {
+        if ((!areTranslucentBarsAllowed() && transWin != mStatusBar)
+                || forceOpaqueSystemBars) {
             vis &= ~(View.NAVIGATION_BAR_TRANSLUCENT | View.STATUS_BAR_TRANSLUCENT
                     | View.SYSTEM_UI_TRANSPARENT);
         }
@@ -6742,24 +6757,21 @@
         // update status bar
         boolean immersiveSticky =
                 (vis & View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY) != 0;
-        boolean hideStatusBarWM =
-                mTopFullscreenOpaqueWindowState != null &&
-                (PolicyControl.getWindowFlags(mTopFullscreenOpaqueWindowState, null)
+        final boolean hideStatusBarWM =
+                mTopFullscreenOpaqueWindowState != null
+                && (PolicyControl.getWindowFlags(mTopFullscreenOpaqueWindowState, null)
                         & WindowManager.LayoutParams.FLAG_FULLSCREEN) != 0;
-        boolean hideStatusBarSysui =
+        final boolean hideStatusBarSysui =
                 (vis & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0;
-        boolean hideNavBarSysui =
+        final boolean hideNavBarSysui =
                 (vis & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0;
 
-        boolean transientStatusBarAllowed =
-                mStatusBar != null && (
-                hideStatusBarWM
-                || (hideStatusBarSysui && immersiveSticky)
-                || statusBarHasFocus);
+        final boolean transientStatusBarAllowed = mStatusBar != null
+                && (statusBarHasFocus || (!forceShowSystemBars
+                        && (hideStatusBarWM || (hideStatusBarSysui && immersiveSticky))));
 
-        boolean transientNavBarAllowed =
-                mNavigationBar != null &&
-                hideNavBarSysui && immersiveSticky;
+        final boolean transientNavBarAllowed = mNavigationBar != null
+                && !forceShowSystemBars && hideNavBarSysui && immersiveSticky;
 
         final long now = SystemClock.uptimeMillis();
         final boolean pendingPanic = mPendingPanicGestureUptime != 0
@@ -6772,11 +6784,11 @@
             mNavigationBarController.showTransient();
         }
 
-        boolean denyTransientStatus = mStatusBarController.isTransientShowRequested()
+        final boolean denyTransientStatus = mStatusBarController.isTransientShowRequested()
                 && !transientStatusBarAllowed && hideStatusBarSysui;
-        boolean denyTransientNav = mNavigationBarController.isTransientShowRequested()
+        final boolean denyTransientNav = mNavigationBarController.isTransientShowRequested()
                 && !transientNavBarAllowed;
-        if (denyTransientStatus || denyTransientNav) {
+        if (denyTransientStatus || denyTransientNav || forceShowSystemBars) {
             // clear the clearable flags instead
             clearClearableFlagsLw();
             vis &= ~View.SYSTEM_UI_CLEARABLE_FLAGS;
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index a0a31c0..ced0433 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -397,6 +397,10 @@
     // Set this to false to disable.
     private boolean mUserInactiveOverrideFromWindowManager;
 
+    // The next possible user activity timeout after being explicitly told the user is inactive.
+    // Set to -1 when not told the user is inactive since the last period spent dozing or asleep.
+    private long mOverriddenTimeout = -1;
+
     // The user activity timeout override from the window manager
     // to allow the current foreground activity to override the user activity timeout.
     // Use -1 to disable.
@@ -1034,6 +1038,7 @@
 
             if (mUserInactiveOverrideFromWindowManager) {
                 mUserInactiveOverrideFromWindowManager = false;
+                mOverriddenTimeout = -1;
             }
 
             if (mWakefulness == WAKEFULNESS_ASLEEP
@@ -1251,12 +1256,28 @@
         }
     }
 
+    /**
+     * Logs the time the device would have spent awake before user activity timeout,
+     * had the system not been told the user was inactive.
+     */
+    private void logSleepTimeoutRecapturedLocked() {
+        final long now = SystemClock.uptimeMillis();
+        final long savedWakeTimeMs = mOverriddenTimeout - now;
+        if (savedWakeTimeMs >= 0) {
+            EventLog.writeEvent(EventLogTags.POWER_SOFT_SLEEP_REQUESTED, savedWakeTimeMs);
+            mOverriddenTimeout = -1;
+        }
+    }
+
     private void finishWakefulnessChangeIfNeededLocked() {
         if (mWakefulnessChanging && mDisplayReady) {
             if (mWakefulness == WAKEFULNESS_DOZING
                     && (mWakeLockSummary & WAKE_LOCK_DOZE) == 0) {
                 return; // wait until dream has enabled dozing
             }
+            if (mWakefulness == WAKEFULNESS_DOZING || mWakefulness == WAKEFULNESS_ASLEEP) {
+                logSleepTimeoutRecapturedLocked();
+            }
             mWakefulnessChanging = false;
             mNotifier.onWakefulnessChangeFinished();
         }
@@ -1579,10 +1600,11 @@
                 if (mUserActivitySummary != USER_ACTIVITY_SCREEN_DREAM && userInactiveOverride) {
                     if ((mUserActivitySummary &
                             (USER_ACTIVITY_SCREEN_BRIGHT | USER_ACTIVITY_SCREEN_DIM)) != 0) {
-                      // Device is being kept awake by recent user activity
-                      long savedWakeTimeMs = now - nextTimeout;
-                      EventLog.writeEvent(
-                              EventLogTags.POWER_SOFT_SLEEP_REQUESTED, savedWakeTimeMs);
+                        // Device is being kept awake by recent user activity
+                        if (nextTimeout >= now && mOverriddenTimeout == -1) {
+                            // Save when the next timeout would have occurred
+                            mOverriddenTimeout = nextTimeout;
+                        }
                     }
                     mUserActivitySummary = USER_ACTIVITY_SCREEN_DREAM;
                     nextTimeout = -1;
diff --git a/services/core/java/com/android/server/wm/DimBehindController.java b/services/core/java/com/android/server/wm/DimBehindController.java
new file mode 100644
index 0000000..8870dd1
--- /dev/null
+++ b/services/core/java/com/android/server/wm/DimBehindController.java
@@ -0,0 +1,299 @@
+package com.android.server.wm;
+
+import static com.android.server.wm.WindowManagerService.DEBUG_DIM_LAYER;
+
+import android.graphics.Rect;
+import android.util.ArrayMap;
+import android.util.Slog;
+import android.util.TypedValue;
+
+import java.io.PrintWriter;
+
+/**
+ * Centralizes the control of dim layers used for
+ * {@link android.view.WindowManager.LayoutParams#FLAG_DIM_BEHIND}.
+ */
+class DimBehindController {
+    private static final String TAG = "DimBehindController";
+
+    /** Amount of time in milliseconds to animate the dim surface from one value to another,
+     * when no window animation is driving it. */
+    private static final int DEFAULT_DIM_DURATION = 200;
+
+    // Shared dim layer for fullscreen users. {@link DimBehindState#dimLayer} will point to this
+    // instead of creating a new object per fullscreen task on a display.
+    private DimLayer mSharedFullScreenDimLayer;
+
+    private ArrayMap<DimLayer.DimLayerUser, DimBehindState> mState = new ArrayMap<>();
+
+    private DisplayContent mDisplayContent;
+
+    private Rect mTmpBounds = new Rect();
+
+    DimBehindController(DisplayContent displayContent) {
+        mDisplayContent = displayContent;
+    }
+
+    /** Updates the dim layer bounds, recreating it if needed. */
+    void updateDimLayer(DimLayer.DimLayerUser dimLayerUser) {
+        DimBehindState state = getOrCreateDimBehindState(dimLayerUser);
+        final boolean previousFullscreen = state.dimLayer != null
+                && state.dimLayer == mSharedFullScreenDimLayer;
+        DimLayer newDimLayer;
+        final int displayId = mDisplayContent.getDisplayId();
+        if (dimLayerUser.isFullscreen()) {
+            if (previousFullscreen) {
+                // Nothing to do here...
+                return;
+            }
+            // Use shared fullscreen dim layer
+            newDimLayer = mSharedFullScreenDimLayer;
+            if (newDimLayer == null) {
+                if (state.dimLayer != null) {
+                    // Re-purpose the previous dim layer.
+                    newDimLayer = state.dimLayer;
+                } else {
+                    // Create new full screen dim layer.
+                    newDimLayer = new DimLayer(mDisplayContent.mService, dimLayerUser, displayId);
+                }
+                dimLayerUser.getBounds(mTmpBounds);
+                newDimLayer.setBounds(mTmpBounds);
+                mSharedFullScreenDimLayer = newDimLayer;
+            } else if (state.dimLayer != null) {
+                state.dimLayer. destroySurface();
+            }
+        } else {
+            newDimLayer = (state.dimLayer == null || previousFullscreen)
+                    ? new DimLayer(mDisplayContent.mService, dimLayerUser, displayId)
+                    : state.dimLayer;
+            dimLayerUser.getBounds(mTmpBounds);
+            newDimLayer.setBounds(mTmpBounds);
+        }
+        state.dimLayer = newDimLayer;
+    }
+
+    private DimBehindState getOrCreateDimBehindState(DimLayer.DimLayerUser dimLayerUser) {
+        if (DEBUG_DIM_LAYER) Slog.v(TAG, "getDimBehindState, dimLayerUser="
+                + dimLayerUser.toShortString());
+        DimBehindState state = mState.get(dimLayerUser);
+        if (state == null) {
+            state = new DimBehindState();
+            mState.put(dimLayerUser, state);
+        }
+        return state;
+    }
+
+    private void setContinueDimming(DimLayer.DimLayerUser dimLayerUser) {
+        DimBehindState state = mState.get(dimLayerUser);
+        if (state == null) {
+            if (DEBUG_DIM_LAYER) Slog.w(TAG, "setContinueDimming, no state for: "
+                    + dimLayerUser.toShortString());
+            return;
+        }
+        state.continueDimming = true;
+    }
+
+    boolean isDimming() {
+        for (int i = mState.size() - 1; i >= 0; i--) {
+            DimBehindState state = mState.valueAt(i);
+            if (state.dimLayer != null && state.dimLayer.isDimming()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    void resetDimming() {
+        for (int i = mState.size() - 1; i >= 0; i--) {
+            mState.valueAt(i).continueDimming = false;
+        }
+    }
+
+    private boolean getContinueDimming(DimLayer.DimLayerUser dimLayerUser) {
+        DimBehindState state = mState.get(dimLayerUser);
+        return state != null && state.continueDimming;
+    }
+
+    void startDimmingIfNeeded(DimLayer.DimLayerUser dimLayerUser,
+            WindowStateAnimator newWinAnimator) {
+        // Only set dim params on the highest dimmed layer.
+        // Don't turn on for an unshown surface, or for any layer but the highest dimmed layer.
+        DimBehindState state = getOrCreateDimBehindState(dimLayerUser);
+        if (DEBUG_DIM_LAYER) Slog.v(TAG, "startDimmingIfNeeded,"
+                + " dimLayerUser=" + dimLayerUser.toShortString()
+                + " newWinAnimator=" + newWinAnimator
+                + " state.animator=" + state.animator);
+        if (newWinAnimator.mSurfaceShown && (state.animator == null
+                || !state.animator.mSurfaceShown
+                || state.animator.mAnimLayer <= newWinAnimator.mAnimLayer)) {
+            state.animator = newWinAnimator;
+            if (state.animator.mWin.mAppToken == null && !dimLayerUser.isFullscreen()) {
+                // Dim should cover the entire screen for system windows.
+                mDisplayContent.getLogicalDisplayRect(mTmpBounds);
+                state.dimLayer.setBounds(mTmpBounds);
+            }
+        }
+    }
+
+    void stopDimmingIfNeeded() {
+        if (DEBUG_DIM_LAYER) Slog.v(TAG, "stopDimmingIfNeeded, mState.size()=" + mState.size());
+        for (int i = mState.size() - 1; i >= 0; i--) {
+            DimLayer.DimLayerUser dimLayerUser = mState.keyAt(i);
+            stopDimmingIfNeeded(dimLayerUser);
+        }
+    }
+
+    private void stopDimmingIfNeeded(DimLayer.DimLayerUser dimLayerUser) {
+        // No need to check if state is null, we know the key has a value.
+        DimBehindState state = mState.get(dimLayerUser);
+        if (DEBUG_DIM_LAYER) Slog.v(TAG, "stopDimmingIfNeeded,"
+                + " dimLayerUser=" + dimLayerUser.toShortString()
+                + " state.continueDimming=" + state.continueDimming
+                + " state.dimLayer.isDimming=" + state.dimLayer.isDimming());
+        if (!state.continueDimming && state.dimLayer.isDimming()) {
+            state.animator = null;
+            dimLayerUser.getBounds(mTmpBounds);
+            state.dimLayer.setBounds(mTmpBounds);
+        }
+    }
+
+    boolean animateDimLayers() {
+        int fullScreen = -1;
+        int fullScreenAndDimming = -1;
+        boolean result = false;
+
+        for (int i = mState.size() - 1; i >= 0; i--) {
+            DimLayer.DimLayerUser user = mState.keyAt(i);
+            if (user.isFullscreen()) {
+                fullScreen = i;
+                if (mState.valueAt(i).continueDimming) {
+                    fullScreenAndDimming = i;
+                }
+            } else {
+                // We always want to animate the non fullscreen windows, they don't share their
+                // dim layers.
+                result |= animateDimLayers(user);
+            }
+        }
+        // For the shared, full screen dim layer, we prefer the animation that is causing it to
+        // appear.
+        if (fullScreenAndDimming != -1) {
+            result |= animateDimLayers(mState.keyAt(fullScreenAndDimming));
+        } else if (fullScreen != -1) {
+            // If there is no animation for the full screen dim layer to appear, we can use any of
+            // the animators that will cause it to disappear.
+            result |= animateDimLayers(mState.keyAt(fullScreen));
+        }
+        return result;
+    }
+
+    private boolean animateDimLayers(DimLayer.DimLayerUser dimLayerUser) {
+        DimBehindState state = mState.get(dimLayerUser);
+        if (DEBUG_DIM_LAYER) Slog.v(TAG, "animateDimLayers,"
+                + " dimLayerUser=" + dimLayerUser.toShortString()
+                + " state.animator=" + state.animator
+                + " state.continueDimming=" + state.continueDimming);
+        final int dimLayer;
+        final float dimAmount;
+        if (state.animator == null) {
+            dimLayer = state.dimLayer.getLayer();
+            dimAmount = 0;
+        } else {
+            dimLayer = state.animator.mAnimLayer - WindowManagerService.LAYER_OFFSET_DIM;
+            dimAmount = state.animator.mWin.mAttrs.dimAmount;
+        }
+        final float targetAlpha = state.dimLayer.getTargetAlpha();
+        if (targetAlpha != dimAmount) {
+            if (state.animator == null) {
+                state.dimLayer.hide(DEFAULT_DIM_DURATION);
+            } else {
+                long duration = (state.animator.mAnimating && state.animator.mAnimation != null)
+                        ? state.animator.mAnimation.computeDurationHint()
+                        : DEFAULT_DIM_DURATION;
+                if (targetAlpha > dimAmount) {
+                    duration = getDimBehindFadeDuration(duration);
+                }
+                state.dimLayer.show(dimLayer, dimAmount, duration);
+            }
+        } else if (state.dimLayer.getLayer() != dimLayer) {
+            state.dimLayer.setLayer(dimLayer);
+        }
+        if (state.dimLayer.isAnimating()) {
+            if (!mDisplayContent.mService.okToDisplay()) {
+                // Jump to the end of the animation.
+                state.dimLayer.show();
+            } else {
+                return state.dimLayer.stepAnimation();
+            }
+        }
+        return false;
+    }
+
+    boolean isDimming(DimLayer.DimLayerUser dimLayerUser, WindowStateAnimator winAnimator) {
+        DimBehindState state = mState.get(dimLayerUser);
+        return state != null && state.animator == winAnimator && state.dimLayer.isDimming();
+    }
+
+    private long getDimBehindFadeDuration(long duration) {
+        TypedValue tv = new TypedValue();
+        mDisplayContent.mService.mContext.getResources().getValue(
+                com.android.internal.R.fraction.config_dimBehindFadeDuration, tv, true);
+        if (tv.type == TypedValue.TYPE_FRACTION) {
+            duration = (long) tv.getFraction(duration, duration);
+        } else if (tv.type >= TypedValue.TYPE_FIRST_INT && tv.type <= TypedValue.TYPE_LAST_INT) {
+            duration = tv.data;
+        }
+        return duration;
+    }
+
+    void close() {
+        for (int i = mState.size() - 1; i >= 0; i--) {
+            DimBehindState state = mState.valueAt(i);
+            state.dimLayer.destroySurface();
+        }
+        mState.clear();
+        mSharedFullScreenDimLayer = null;
+    }
+
+    void removeDimLayerUser(DimLayer.DimLayerUser dimLayerUser) {
+        mState.remove(dimLayerUser);
+    }
+
+    void applyDimBehind(DimLayer.DimLayerUser dimLayerUser, WindowStateAnimator animator) {
+        if (dimLayerUser == null) {
+            Slog.e(TAG, "Trying to apply dim layer for: " + this
+                    + ", but no dim layer user found.");
+            return;
+        }
+        if (!getContinueDimming(dimLayerUser)) {
+            setContinueDimming(dimLayerUser);
+            if (!isDimming(dimLayerUser, animator)) {
+                if (DEBUG_DIM_LAYER) Slog.v(TAG, "Win " + this + " start dimming.");
+                startDimmingIfNeeded(dimLayerUser, animator);
+            }
+        }
+    }
+
+    private static class DimBehindState {
+        // The particular window with FLAG_DIM_BEHIND set. If null, hide dimLayer.
+        WindowStateAnimator animator;
+        // Set to false at the start of performLayoutAndPlaceSurfaces. If it is still false by the
+        // end then stop any dimming.
+        boolean continueDimming;
+        DimLayer dimLayer;
+    }
+
+    void dump(String prefix, PrintWriter pw) {
+        pw.println(prefix + "DimBehindController");
+        for (int i = 0, n = mState.size(); i < n; i++) {
+            pw.println(prefix + "  " + mState.keyAt(i).toShortString());
+            pw.print(prefix + "    ");
+            DimBehindState state = mState.valueAt(i);
+            pw.print("dimLayer=" + (state.dimLayer == mSharedFullScreenDimLayer ? "shared" :
+                    state.dimLayer));
+            pw.print(", animator=" + state.animator);
+            pw.println(", continueDimming=" + state.continueDimming + "}");
+
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/wm/DimLayer.java b/services/core/java/com/android/server/wm/DimLayer.java
index 538d6b84..bc31274 100644
--- a/services/core/java/com/android/server/wm/DimLayer.java
+++ b/services/core/java/com/android/server/wm/DimLayer.java
@@ -29,6 +29,10 @@
     private static final String TAG = "DimLayer";
     private static final boolean DEBUG = false;
 
+    public static final float RESIZING_HINT_ALPHA = 0.5f;
+
+    public static final int RESIZING_HINT_DURATION_MS = 0;
+
     /** Actual surface that dims */
     SurfaceControl mDimSurface;
 
@@ -65,6 +69,9 @@
         boolean isFullscreen();
         /** Returns the display info. of the dim layer user. */
         DisplayInfo getDisplayInfo();
+        /** Gets the bounds of the dim layer user. */
+        void getBounds(Rect outBounds);
+        String toShortString();
     }
     /** The user of this dim layer. */
     final DimLayerUser mUser;
@@ -239,8 +246,9 @@
                 mDuration = duration;
             }
         }
-        if (DEBUG) Slog.v(TAG, "show: mStartAlpha=" + mStartAlpha + " mStartTime=" + mStartTime);
         mTargetAlpha = alpha;
+        if (DEBUG) Slog.v(TAG, "show: mStartAlpha=" + mStartAlpha + " mStartTime=" + mStartTime
+                + " mTargetAlpha=" + mTargetAlpha);
     }
 
     /** Immediate hide.
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 745874c..438658e 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -115,6 +115,8 @@
 
     final DockedStackDividerController mDividerControllerLocked;
 
+    final DimBehindController mDimBehindController;
+
     /**
      * @param display May not be null.
      * @param service You know.
@@ -128,6 +130,7 @@
         mService = service;
         initializeDisplayBaseInfo();
         mDividerControllerLocked = new DockedStackDividerController(service.mContext, this);
+        mDimBehindController = new DimBehindController(this);
     }
 
     int getDisplayId() {
@@ -246,6 +249,7 @@
     }
 
     void detachStack(TaskStack stack) {
+        mDimBehindController.removeDimLayerUser(stack);
         mStacks.remove(stack);
     }
 
@@ -262,9 +266,16 @@
             final ArrayList<Task> tasks = mStacks.get(stackNdx).getTasks();
             for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
                 final Task task = tasks.get(taskNdx);
-                task.getBounds(mTmpRect);
-                if (mTmpRect.contains(x, y)) {
-                    return task.mTaskId;
+                // We need to use the visible frame on the window for any touch-related tests.
+                // Can't use the task's bounds because the original task bounds might be adjusted
+                // to fit the content frame. For example, the presence of the IME adjusting the
+                // windows frames when the app window is the IME target.
+                final WindowState win = task.getTopAppMainWindow();
+                if (win != null) {
+                    win.getVisibleBounds(mTmpRect, !BOUNDS_FOR_TOUCH);
+                    if (mTmpRect.contains(x, y)) {
+                        return task.mTaskId;
+                    }
                 }
             }
         }
@@ -294,7 +305,7 @@
                 // might be adjusted to fit the content frame. (One example is when the
                 // task is put to top-left quadrant, the actual visible frame would not
                 // start at (0,0) after it's adjusted for the status bar.)
-                WindowState win = task.getTopAppMainWindow();
+                final WindowState win = task.getTopAppMainWindow();
                 if (win != null) {
                     win.getVisibleBounds(mTmpRect, !BOUNDS_FOR_TOUCH);
                     mTmpRect.inset(-delta, -delta);
@@ -382,54 +393,23 @@
     }
 
     boolean animateDimLayers() {
-        boolean result = false;
-        for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
-            final ArrayList<Task> tasks = mStacks.get(stackNdx).getTasks();
-            for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
-                final Task task = tasks.get(taskNdx);
-                result |= task.animateDimLayers();
-                if (task.isFullscreen()) {
-                    // No point in continuing as this task covers the entire screen.
-                    // Also, fullscreen tasks all share the same dim layer, so we don't want
-                    // processing of fullscreen task below this one affecting the dim layer state.
-                    return result;
-                }
-            }
-        }
-        return result;
+        return mDimBehindController.animateDimLayers();
     }
 
     void resetDimming() {
-        for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
-            final ArrayList<Task> tasks = mStacks.get(stackNdx).getTasks();
-            for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
-                tasks.get(taskNdx).clearContinueDimming();
-            }
-        }
+        mDimBehindController.resetDimming();
     }
 
     boolean isDimming() {
-        for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
-            final ArrayList<Task> tasks = mStacks.get(stackNdx).getTasks();
-            for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
-                if (tasks.get(taskNdx).isDimming()) {
-                    return true;
-                }
-            }
-        }
-        return false;
+        return mDimBehindController.isDimming();
     }
 
     void stopDimmingIfNeeded() {
-        for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
-            final ArrayList<Task> tasks = mStacks.get(stackNdx).getTasks();
-            for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
-                tasks.get(taskNdx).stopDimmingIfNeeded();
-            }
-        }
+        mDimBehindController.stopDimmingIfNeeded();
     }
 
     void close() {
+        mDimBehindController.close();
         for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
             mStacks.get(stackNdx).close();
         }
@@ -576,6 +556,7 @@
             }
         }
         pw.println();
+        mDimBehindController.dump(prefix + "  ", pw);
     }
 
     @Override
@@ -584,12 +565,7 @@
     }
 
     TaskStack getDockedStackLocked() {
-        for (int i = mStacks.size() - 1; i >= 0; i--) {
-            TaskStack stack = mStacks.get(i);
-            if (stack.mStackId == DOCKED_STACK_ID && stack.isVisibleLocked()) {
-                return stack;
-            }
-        }
-        return null;
+        final TaskStack stack = mService.mStackIdToStack.get(DOCKED_STACK_ID);
+        return (stack != null && stack.isVisibleLocked()) ? stack : null;
     }
 }
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index 8c5d319..610524f 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -17,6 +17,7 @@
 package com.android.server.wm;
 
 import static android.app.ActivityManager.DOCKED_STACK_ID;
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
 import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
 import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
@@ -24,17 +25,22 @@
 import static android.view.WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING;
 import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
 import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
+import static com.android.server.wm.DimLayer.RESIZING_HINT_ALPHA;
+import static com.android.server.wm.DimLayer.RESIZING_HINT_DURATION_MS;
+import static com.android.server.wm.TaskPositioner.SIDE_MARGIN_DIP;
 import static com.android.server.wm.TaskStack.DOCKED_BOTTOM;
-import static com.android.server.wm.TaskStack.DOCKED_INVALID;
 import static com.android.server.wm.TaskStack.DOCKED_LEFT;
 import static com.android.server.wm.TaskStack.DOCKED_RIGHT;
 import static com.android.server.wm.TaskStack.DOCKED_TOP;
+import static com.android.server.wm.WindowManagerService.dipToPixel;
 
 import android.content.Context;
+import android.content.res.Configuration;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
 import android.os.RemoteException;
 import android.util.Slog;
+import android.view.DisplayInfo;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.View;
@@ -44,11 +50,15 @@
 /**
  * Controls showing and hiding of a docked stack divider on the display.
  */
-public class DockedStackDividerController implements View.OnTouchListener {
+public class DockedStackDividerController implements View.OnTouchListener, DimLayer.DimLayerUser {
     private static final String TAG = "DockedStackDivider";
     private final Context mContext;
     private final int mDividerWidth;
     private final DisplayContent mDisplayContent;
+    private final int mSideMargin;
+    private final DimLayer mDimLayer;
+    private final int mDisplayWidth;
+    private final int mDisplayHeight;
     private View mView;
     private Rect mTmpRect = new Rect();
     private Rect mLastResizeRect = new Rect();
@@ -57,22 +67,30 @@
     private TaskStack mTaskStack;
     private Rect mOriginalRect = new Rect();
     private int mDockSide;
-
+    private boolean mDimLayerVisible;
 
     DockedStackDividerController(Context context, DisplayContent displayContent) {
         mContext = context;
         mDisplayContent = displayContent;
+        final DisplayInfo info = displayContent.getDisplayInfo();
+        mDisplayWidth = info.logicalWidth;
+        mDisplayHeight = info.logicalHeight;
         mDividerWidth = context.getResources().getDimensionPixelSize(
                 com.android.internal.R.dimen.docked_stack_divider_thickness);
+        mSideMargin = dipToPixel(SIDE_MARGIN_DIP, mDisplayContent.getDisplayMetrics());
+        mDimLayer = new DimLayer(displayContent.mService, this, displayContent.getDisplayId());
     }
 
-    private void addDivider() {
+    private void addDivider(Configuration configuration) {
         View view = LayoutInflater.from(mContext).inflate(
                 com.android.internal.R.layout.docked_stack_divider, null);
         view.setOnTouchListener(this);
         WindowManagerGlobal manager = WindowManagerGlobal.getInstance();
+        final boolean landscape = configuration.orientation == ORIENTATION_LANDSCAPE;
+        final int width = landscape ? mDividerWidth : MATCH_PARENT;
+        final int height = landscape ? MATCH_PARENT : mDividerWidth;
         WindowManager.LayoutParams params = new WindowManager.LayoutParams(
-                mDividerWidth, MATCH_PARENT, TYPE_DOCK_DIVIDER,
+                width, height, TYPE_DOCK_DIVIDER,
                 FLAG_TOUCHABLE_WHEN_WAKING | FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL
                         | FLAG_WATCH_OUTSIDE_TOUCH | FLAG_SPLIT_TOUCH,
                 PixelFormat.OPAQUE);
@@ -92,10 +110,13 @@
         return mView != null;
     }
 
-    void update() {
+    void update(Configuration configuration, boolean forceUpdate) {
+        if (forceUpdate && mView != null) {
+            removeDivider();
+        }
         TaskStack stack = mDisplayContent.getDockedStackLocked();
         if (stack != null && mView == null) {
-            addDivider();
+            addDivider(configuration);
         } else if (stack == null && mView != null) {
             removeDivider();
         }
@@ -149,22 +170,175 @@
                 break;
             case MotionEvent.ACTION_MOVE:
                 if (mTaskStack != null) {
-                    resizeStack(event);
+                    final int x = (int) event.getRawX();
+                    final int y = (int) event.getRawY();
+                    resizeStack(x, y);
                 }
                 break;
             case MotionEvent.ACTION_UP:
             case MotionEvent.ACTION_CANCEL:
-                mTaskStack = null;
+                if (mTaskStack != null) {
+                    final int x = (int) event.getRawX();
+                    final int y = (int) event.getRawY();
+                    // At most one of these will be executed, the other one will exit early.
+                    maybeDismissTaskStack(x, y);
+                    maybeMaximizeTaskStack(x, y);
+                    mTaskStack = null;
+                }
+                setDimLayerVisible(false);
                 mDockSide = TaskStack.DOCKED_INVALID;
                 break;
         }
         return true;
     }
 
-    private void resizeStack(MotionEvent event) {
+    private void maybeMaximizeTaskStack(int x, int y) {
+        final int distance = distanceFromFullScreen(mDockSide, x, y);
+        if (distance == -1) {
+            Slog.wtf(TAG, "maybeMaximizeTaskStack: Unknown dock side=" + mDockSide);
+            return;
+        }
+        if (distance <= mSideMargin) {
+            try {
+                mDisplayContent.mService.mActivityManager.resizeStack(mTaskStack.mStackId, null);
+            } catch (RemoteException e) {
+                // This can't happen because we are in the same process.
+            }
+        }
+    }
+
+    private void maybeDismissTaskStack(int x, int y) {
+        final int distance = distanceFromDockSide(mDockSide, mOriginalRect, x, y);
+        if (distance == -1) {
+            Slog.wtf(TAG, "maybeDismissTaskStack: Unknown dock side=" + mDockSide);
+            return;
+        }
+        if (distance <= mSideMargin) {
+            try {
+                mDisplayContent.mService.mActivityManager.removeStack(mTaskStack.mStackId);
+            } catch (RemoteException e) {
+                // This can't happen because we are in the same process.
+            }
+        }
+    }
+
+    private void updateDimLayer(int x, int y) {
+        final int dismissDistance = distanceFromDockSide(mDockSide, mOriginalRect, x, y);
+        final int maximizeDistance = distanceFromFullScreen(mDockSide, x, y);
+        if (dismissDistance == -1 || maximizeDistance == -1) {
+            Slog.wtf(TAG, "updateDimLayer: Unknown dock side=" + mDockSide);
+            return;
+        }
+        if (dismissDistance <= mSideMargin && maximizeDistance <= mSideMargin) {
+            Slog.wtf(TAG, "Both dismiss and maximize distances would trigger dim layer.");
+            return;
+        }
+        if (dismissDistance <= mSideMargin) {
+            setDismissDimLayerVisible(x, y);
+        } else if (maximizeDistance <= mSideMargin) {
+            setMaximizeDimLayerVisible(x, y);
+        } else {
+            setDimLayerVisible(false);
+        }
+    }
+
+    /**
+     * Provides the distance from the point to the docked side of a rectangle.
+     *
+     * @return non negative distance or -1 on error
+     */
+    private static int distanceFromDockSide(int dockSide, Rect bounds, int x, int y) {
+        switch (dockSide) {
+            case DOCKED_LEFT:
+                return x - bounds.left;
+            case DOCKED_TOP:
+                return y - bounds.top;
+            case DOCKED_RIGHT:
+                return bounds.right - x;
+            case DOCKED_BOTTOM:
+                return bounds.bottom - y;
+        }
+        return -1;
+    }
+
+    private int distanceFromFullScreen(int dockSide, int x, int y) {
+        switch (dockSide) {
+            case DOCKED_LEFT:
+                return mDisplayWidth - x;
+            case DOCKED_TOP:
+                return mDisplayHeight - y;
+            case DOCKED_RIGHT:
+                return x;
+            case DOCKED_BOTTOM:
+                return y;
+        }
+        return -1;
+    }
+
+    private void setDismissDimLayerVisible(int x, int y) {
         mTmpRect.set(mOriginalRect);
-        final int deltaX = (int) event.getRawX() - mStartX;
-        final int deltaY = (int) event.getRawY() - mStartY;
+        switch (mDockSide) {
+            case DOCKED_LEFT:
+                mTmpRect.right = x;
+                break;
+            case DOCKED_TOP:
+                mTmpRect.bottom = y;
+                break;
+            case DOCKED_RIGHT:
+                mTmpRect.left = x;
+                break;
+            case DOCKED_BOTTOM:
+                mTmpRect.top = y;
+                break;
+            default:
+                Slog.wtf(TAG, "setDismissDimLayerVisible: Unknown dock side when setting dim "
+                        + "layer=" + mDockSide);
+                return;
+        }
+        mDimLayer.setBounds(mTmpRect);
+        setDimLayerVisible(true);
+    }
+
+    private void setMaximizeDimLayerVisible(int x, int y) {
+        mTmpRect.set(0, 0, mDisplayWidth, mDisplayHeight);
+        switch (mDockSide) {
+            case DOCKED_LEFT:
+                mTmpRect.left = x;
+                break;
+            case DOCKED_TOP:
+                mTmpRect.top = y;
+                break;
+            case DOCKED_RIGHT:
+                mTmpRect.right = x;
+                break;
+            case DOCKED_BOTTOM:
+                mTmpRect.top = y;
+                break;
+            default:
+                Slog.wtf(TAG, "setMaximizeDimLayerVisible: Unknown dock side when setting dim "
+                        + "layer=" + mDockSide);
+        }
+        mDimLayer.setBounds(mTmpRect);
+        setDimLayerVisible(true);
+    }
+
+    private void setDimLayerVisible(boolean visible) {
+        if (mDimLayerVisible == visible) {
+            return;
+        }
+        mDimLayerVisible = visible;
+        if (mDimLayerVisible) {
+            mDimLayer.show(mDisplayContent.mService.getDragLayerLocked(), RESIZING_HINT_ALPHA,
+                    RESIZING_HINT_DURATION_MS);
+        } else {
+            mDimLayer.hide();
+        }
+    }
+
+    private void resizeStack(int x, int y) {
+        mTmpRect.set(mOriginalRect);
+        final int deltaX = x - mStartX;
+        final int deltaY = y - mStartY;
         switch (mDockSide) {
             case DOCKED_LEFT:
                 mTmpRect.right += deltaX;
@@ -186,7 +360,9 @@
         try {
             mDisplayContent.mService.mActivityManager.resizeStack(DOCKED_STACK_ID, mTmpRect);
         } catch (RemoteException e) {
+            // This can't happen because we are in the same process.
         }
+        updateDimLayer(x, y);
     }
 
     boolean isResizing() {
@@ -196,4 +372,24 @@
     int getWidthAdjustment() {
         return getWidth() / 2;
     }
+
+    @Override
+    public boolean isFullscreen() {
+        return false;
+    }
+
+    @Override
+    public DisplayInfo getDisplayInfo() {
+        return mDisplayContent.getDisplayInfo();
+    }
+
+    @Override
+    public void getBounds(Rect outBounds) {
+        // This dim layer user doesn't need this.
+    }
+
+    @Override
+    public String toShortString() {
+        return TAG;
+    }
 }
diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
index 7dd716e..283d498 100644
--- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -18,8 +18,9 @@
 
 import java.io.PrintWriter;
 
+import static com.android.server.wm.WindowManagerService.TYPE_LAYER_MULTIPLIER;
 import static com.android.server.wm.WindowStateAnimator.SurfaceTrace;
-
+import static com.android.server.wm.WindowStateAnimator.WINDOW_FREEZE_LAYER;
 import android.content.Context;
 import android.graphics.Matrix;
 import android.graphics.PixelFormat;
@@ -42,7 +43,15 @@
     static final boolean TWO_PHASE_ANIMATION = false;
     static final boolean USE_CUSTOM_BLACK_FRAME = false;
 
-    static final int FREEZE_LAYER = WindowManagerService.TYPE_LAYER_MULTIPLIER * 200;
+    /*
+     * Layers for screen rotation animation. We put these layers above
+     * WINDOW_FREEZE_LAYER so that screen freeze will cover all windows.
+     */
+    static final int SCREEN_FREEZE_LAYER_BASE       = WINDOW_FREEZE_LAYER + TYPE_LAYER_MULTIPLIER;
+    static final int SCREEN_FREEZE_LAYER_ENTER      = SCREEN_FREEZE_LAYER_BASE;
+    static final int SCREEN_FREEZE_LAYER_SCREENSHOT = SCREEN_FREEZE_LAYER_BASE + 1;
+    static final int SCREEN_FREEZE_LAYER_EXIT       = SCREEN_FREEZE_LAYER_BASE + 2;
+    static final int SCREEN_FREEZE_LAYER_CUSTOM     = SCREEN_FREEZE_LAYER_BASE + 3;
 
     final Context mContext;
     final DisplayContent mDisplayContent;
@@ -265,7 +274,7 @@
                 SurfaceControl.screenshot(SurfaceControl.getBuiltInDisplay(
                         SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN), sur);
                 mSurfaceControl.setLayerStack(display.getLayerStack());
-                mSurfaceControl.setLayer(FREEZE_LAYER + 1);
+                mSurfaceControl.setLayer(SCREEN_FREEZE_LAYER_SCREENSHOT);
                 mSurfaceControl.setAlpha(0);
                 mSurfaceControl.show();
                 sur.destroy();
@@ -545,8 +554,8 @@
                 Rect outer = new Rect(-mOriginalWidth*1, -mOriginalHeight*1,
                         mOriginalWidth*2, mOriginalHeight*2);
                 Rect inner = new Rect(0, 0, mOriginalWidth, mOriginalHeight);
-                mCustomBlackFrame = new BlackFrame(session, outer, inner, FREEZE_LAYER + 3,
-                        layerStack, false);
+                mCustomBlackFrame = new BlackFrame(session, outer, inner,
+                        SCREEN_FREEZE_LAYER_CUSTOM, layerStack, false);
                 mCustomBlackFrame.setMatrix(mFrameInitialMatrix);
             } catch (OutOfResourcesException e) {
                 Slog.w(TAG, "Unable to allocate black surface", e);
@@ -585,8 +594,8 @@
                             mOriginalWidth*2, mOriginalHeight*2);
                     inner = new Rect(0, 0, mOriginalWidth, mOriginalHeight);
                 }
-                mExitingBlackFrame = new BlackFrame(session, outer, inner, FREEZE_LAYER + 2,
-                        layerStack, mForceDefaultOrientation);
+                mExitingBlackFrame = new BlackFrame(session, outer, inner,
+                        SCREEN_FREEZE_LAYER_EXIT, layerStack, mForceDefaultOrientation);
                 mExitingBlackFrame.setMatrix(mFrameInitialMatrix);
             } catch (OutOfResourcesException e) {
                 Slog.w(TAG, "Unable to allocate black surface", e);
@@ -608,8 +617,8 @@
                 Rect outer = new Rect(-finalWidth*1, -finalHeight*1,
                         finalWidth*2, finalHeight*2);
                 Rect inner = new Rect(0, 0, finalWidth, finalHeight);
-                mEnteringBlackFrame = new BlackFrame(session, outer, inner, FREEZE_LAYER,
-                        layerStack, false);
+                mEnteringBlackFrame = new BlackFrame(session, outer, inner,
+                        SCREEN_FREEZE_LAYER_ENTER, layerStack, false);
             } catch (OutOfResourcesException e) {
                 Slog.w(TAG, "Unable to allocate black surface", e);
             } finally {
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 1f986dd..c4600e0 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -29,7 +29,6 @@
 import android.util.EventLog;
 import android.util.Slog;
 import android.util.SparseArray;
-import android.util.TypedValue;
 import android.view.DisplayInfo;
 import android.view.Surface;
 
@@ -39,10 +38,6 @@
 import java.util.ArrayList;
 
 class Task implements DimLayer.DimLayerUser {
-    /** Amount of time in milliseconds to animate the dim surface from one value to another,
-     * when no window animation is driving it. */
-    private static final int DEFAULT_DIM_DURATION = 200;
-
     // Return value from {@link setBounds} indicating no change was made to the Task bounds.
     static final int BOUNDS_CHANGE_NONE = 0;
     // Return value from {@link setBounds} indicating the position of the Task bounds changed.
@@ -78,17 +73,6 @@
     // Whether the task is currently being drag-resized
     private boolean mDragResizing;
 
-    // The particular window with FLAG_DIM_BEHIND set. If null, hide mDimLayer.
-    WindowStateAnimator mDimWinAnimator;
-    // Used to support {@link android.view.WindowManager.LayoutParams#FLAG_DIM_BEHIND}
-    private DimLayer mDimLayer;
-    // Set to false at the start of performLayoutAndPlaceSurfaces. If it is still false by the end
-    // then stop any dimming.
-    private boolean mContinueDimming;
-    // Shared dim layer for fullscreen tasks. {@link #mDimLayer} will point to this instead
-    // of creating a new object per fullscreen task on a display.
-    private static final SparseArray<DimLayer> sSharedFullscreenDimLayers = new SparseArray<>();
-
     Task(int taskId, TaskStack stack, int userId, WindowManagerService service, Rect bounds,
             Configuration config) {
         mTaskId = taskId;
@@ -128,6 +112,10 @@
         if (DEBUG_STACK) Slog.i(TAG, "removeTask: removing taskId=" + mTaskId);
         EventLog.writeEvent(EventLogTags.WM_TASK_REMOVED, mTaskId, "removeTask");
         mDeferRemoval = false;
+        DisplayContent content = getDisplayContent();
+        if (content != null) {
+            content.mDimBehindController.removeDimLayerUser(this);
+        }
         mStack.removeTask(this);
         mService.mTaskIdToTask.delete(mTaskId);
     }
@@ -228,7 +216,9 @@
 
         mBounds.set(bounds);
         mRotation = rotation;
-        updateDimLayer();
+        if (displayContent != null) {
+            displayContent.mDimBehindController.updateDimLayer(this);
+        }
         mOverrideConfig = mFullscreen ? Configuration.EMPTY : config;
         return boundsChange;
     }
@@ -261,7 +251,8 @@
     }
 
     /** Bounds of the task with other system factors taken into consideration. */
-    void getBounds(Rect out) {
+    @Override
+    public void getBounds(Rect out) {
         if (useCurrentBounds()) {
             // No need to adjust the output bounds if fullscreen or the docked stack is visible
             // since it is already what we want to represent to the rest of the system.
@@ -303,142 +294,12 @@
         if (setBounds(mTmpRect2, mOverrideConfig) != BOUNDS_CHANGE_NONE) {
             // Post message to inform activity manager of the bounds change simulating
             // a one-way call. We do this to prevent a deadlock between window manager
-            // lock and activity manager lock been held.
-            mService.mH.sendMessage(mService.mH.obtainMessage(
-                            RESIZE_TASK, mTaskId, RESIZE_MODE_SYSTEM_SCREEN_ROTATION, mBounds));
-        }
-    }
-
-    /** Updates the dim layer bounds, recreating it if needed. */
-    private void updateDimLayer() {
-        DimLayer newDimLayer;
-        final boolean previousFullscreen =
-                mDimLayer != null && sSharedFullscreenDimLayers.indexOfValue(mDimLayer) > -1;
-        final int displayId = mStack.getDisplayContent().getDisplayId();
-        if (mFullscreen) {
-            if (previousFullscreen) {
-                // Nothing to do here...
-                return;
+            // lock and activity manager lock been held. Only tasks within the freeform stack
+            // are resizeable independently of their stack resizing.
+            if (mStack.mStackId == FREEFORM_WORKSPACE_STACK_ID) {
+                mService.mH.sendMessage(mService.mH.obtainMessage(
+                        RESIZE_TASK, mTaskId, RESIZE_MODE_SYSTEM_SCREEN_ROTATION, mBounds));
             }
-            // Use shared fullscreen dim layer
-            newDimLayer = sSharedFullscreenDimLayers.get(displayId);
-            if (newDimLayer == null) {
-                if (mDimLayer != null) {
-                    // Re-purpose the previous dim layer.
-                    newDimLayer = mDimLayer;
-                } else {
-                    // Create new full screen dim layer.
-                    newDimLayer = new DimLayer(mService, this, displayId);
-                }
-                newDimLayer.setBounds(mBounds);
-                sSharedFullscreenDimLayers.put(displayId, newDimLayer);
-            } else if (mDimLayer != null) {
-                mDimLayer.destroySurface();
-            }
-        } else {
-            newDimLayer = (mDimLayer == null || previousFullscreen)
-                    ? new DimLayer(mService, this, displayId) : mDimLayer;
-            newDimLayer.setBounds(mBounds);
-        }
-        mDimLayer = newDimLayer;
-    }
-
-    boolean animateDimLayers() {
-        final int dimLayer;
-        final float dimAmount;
-        if (mDimWinAnimator == null) {
-            dimLayer = mDimLayer.getLayer();
-            dimAmount = 0;
-        } else {
-            dimLayer = mDimWinAnimator.mAnimLayer - WindowManagerService.LAYER_OFFSET_DIM;
-            dimAmount = mDimWinAnimator.mWin.mAttrs.dimAmount;
-        }
-        final float targetAlpha = mDimLayer.getTargetAlpha();
-        if (targetAlpha != dimAmount) {
-            if (mDimWinAnimator == null) {
-                mDimLayer.hide(DEFAULT_DIM_DURATION);
-            } else {
-                long duration = (mDimWinAnimator.mAnimating && mDimWinAnimator.mAnimation != null)
-                        ? mDimWinAnimator.mAnimation.computeDurationHint()
-                        : DEFAULT_DIM_DURATION;
-                if (targetAlpha > dimAmount) {
-                    duration = getDimBehindFadeDuration(duration);
-                }
-                mDimLayer.show(dimLayer, dimAmount, duration);
-            }
-        } else if (mDimLayer.getLayer() != dimLayer) {
-            mDimLayer.setLayer(dimLayer);
-        }
-        if (mDimLayer.isAnimating()) {
-            if (!mService.okToDisplay()) {
-                // Jump to the end of the animation.
-                mDimLayer.show();
-            } else {
-                return mDimLayer.stepAnimation();
-            }
-        }
-        return false;
-    }
-
-    private long getDimBehindFadeDuration(long duration) {
-        TypedValue tv = new TypedValue();
-        mService.mContext.getResources().getValue(
-                com.android.internal.R.fraction.config_dimBehindFadeDuration, tv, true);
-        if (tv.type == TypedValue.TYPE_FRACTION) {
-            duration = (long)tv.getFraction(duration, duration);
-        } else if (tv.type >= TypedValue.TYPE_FIRST_INT && tv.type <= TypedValue.TYPE_LAST_INT) {
-            duration = tv.data;
-        }
-        return duration;
-    }
-
-    void clearContinueDimming() {
-        mContinueDimming = false;
-    }
-
-    void setContinueDimming() {
-        mContinueDimming = true;
-    }
-
-    boolean getContinueDimming() {
-        return mContinueDimming;
-    }
-
-    boolean isDimming() {
-        return mDimLayer.isDimming();
-    }
-
-    boolean isDimming(WindowStateAnimator winAnimator) {
-        return mDimWinAnimator == winAnimator && isDimming();
-    }
-
-    void startDimmingIfNeeded(WindowStateAnimator newWinAnimator) {
-        // Only set dim params on the highest dimmed layer.
-        // Don't turn on for an unshown surface, or for any layer but the highest dimmed layer.
-        if (newWinAnimator.mSurfaceShown && (mDimWinAnimator == null
-                || !mDimWinAnimator.mSurfaceShown
-                || mDimWinAnimator.mAnimLayer < newWinAnimator.mAnimLayer)) {
-            mDimWinAnimator = newWinAnimator;
-            if (mDimWinAnimator.mWin.mAppToken == null
-                    && !mFullscreen && mStack.getDisplayContent() != null) {
-                // Dim should cover the entire screen for system windows.
-                mStack.getDisplayContent().getLogicalDisplayRect(mTmpRect);
-                mDimLayer.setBounds(mTmpRect);
-            }
-        }
-    }
-
-    void stopDimmingIfNeeded() {
-        if (!mContinueDimming && isDimming()) {
-            mDimWinAnimator = null;
-            mDimLayer.setBounds(mBounds);
-        }
-    }
-
-    void close() {
-        if (mDimLayer != null) {
-            mDimLayer.destroySurface();
-            mDimLayer = null;
         }
     }
 
@@ -491,16 +352,14 @@
         return "{taskId=" + mTaskId + " appTokens=" + mAppTokens + " mdr=" + mDeferRemoval + "}";
     }
 
+    @Override
+    public String toShortString() {
+        return "Task=" + mTaskId;
+    }
+
     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);
-        if (mDimLayer.isDimming()) {
-            pw.print(prefix); pw.println("mDimLayer:");
-            mDimLayer.printTo(prefix + " ", pw);
-            pw.print(prefix); pw.print("mDimWinAnimator="); pw.println(mDimWinAnimator);
-        } else {
-            pw.println();
-        }
     }
 }
diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java
index df2e5e8..227b3f0 100644
--- a/services/core/java/com/android/server/wm/TaskPositioner.java
+++ b/services/core/java/com/android/server/wm/TaskPositioner.java
@@ -19,12 +19,15 @@
 import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT;
 import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
 import static android.app.ActivityManager.FREEFORM_WORKSPACE_STACK_ID;
-import static android.app.ActivityManager.RESIZE_MODE_FORCED;
 import static android.app.ActivityManager.RESIZE_MODE_USER;
+import static android.app.ActivityManager.RESIZE_MODE_USER_FORCED;
 import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
+import static com.android.server.wm.DimLayer.RESIZING_HINT_ALPHA;
+import static com.android.server.wm.DimLayer.RESIZING_HINT_DURATION_MS;
 import static com.android.server.wm.WindowManagerService.DEBUG_TASK_POSITIONING;
 import static com.android.server.wm.WindowManagerService.SHOW_TRANSACTIONS;
+import static com.android.server.wm.WindowManagerService.dipToPixel;
 import static com.android.server.wm.WindowState.MINIMUM_VISIBLE_HEIGHT_IN_DP;
 import static com.android.server.wm.WindowState.MINIMUM_VISIBLE_WIDTH_IN_DP;
 
@@ -60,7 +63,7 @@
 
     // The margin the pointer position has to be within the side of the screen to be
     // considered at the side of the screen.
-    private static final int SIDE_MARGIN_DIP = 100;
+    static final int SIDE_MARGIN_DIP = 100;
 
     @IntDef(flag = true,
             value = {
@@ -146,13 +149,18 @@
                         }
                         synchronized (mService.mWindowMap) {
                             mDragEnded = notifyMoveLocked(newX, newY);
+                            mTask.getBounds(mTmpRect);
                         }
-                        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "wm.TaskPositioner.resizeTask");
-                        try {
-                            mService.mActivityManager.resizeTask(
-                                    mTask.mTaskId, mWindowDragBounds, RESIZE_MODE_USER);
-                        } catch(RemoteException e) {}
-                        Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
+                        if (!mTmpRect.equals(mWindowDragBounds)) {
+                            Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER,
+                                    "wm.TaskPositioner.resizeTask");
+                            try {
+                                mService.mActivityManager.resizeTask(
+                                        mTask.mTaskId, mWindowDragBounds, RESIZE_MODE_USER);
+                            } catch (RemoteException e) {
+                            }
+                            Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
+                        }
                     } break;
 
                     case MotionEvent.ACTION_UP: {
@@ -171,15 +179,16 @@
                 }
 
                 if (mDragEnded) {
+                    final boolean wasResizing = mResizing;
                     synchronized (mService.mWindowMap) {
                         endDragLocked();
                     }
                     try {
-                        if (mResizing) {
+                        if (wasResizing) {
                             // We were using fullscreen surface during resizing. Request
                             // resizeTask() one last time to restore surface to window size.
                             mService.mActivityManager.resizeTask(
-                                    mTask.mTaskId, mWindowDragBounds, RESIZE_MODE_FORCED);
+                                    mTask.mTaskId, mWindowDragBounds, RESIZE_MODE_USER_FORCED);
                         }
 
                         if (mCurrentDimSide != CTRL_NONE) {
@@ -240,7 +249,7 @@
                 mDisplay.getDisplayId());
         mDragWindowHandle.name = TAG;
         mDragWindowHandle.inputChannel = mServerChannel;
-        mDragWindowHandle.layer = getDragLayerLocked();
+        mDragWindowHandle.layer = mService.getDragLayerLocked();
         mDragWindowHandle.layoutParamsFlags = 0;
         mDragWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_DRAG;
         mDragWindowHandle.dispatchingTimeoutNanos =
@@ -273,9 +282,9 @@
         mService.pauseRotationLocked();
 
         mDimLayer = new DimLayer(mService, this, mDisplay.getDisplayId());
-        mSideMargin = mService.dipToPixel(SIDE_MARGIN_DIP, mDisplayMetrics);
-        mMinVisibleWidth = mService.dipToPixel(MINIMUM_VISIBLE_WIDTH_IN_DP, mDisplayMetrics);
-        mMinVisibleHeight = mService.dipToPixel(MINIMUM_VISIBLE_HEIGHT_IN_DP, mDisplayMetrics);
+        mSideMargin = dipToPixel(SIDE_MARGIN_DIP, mDisplayMetrics);
+        mMinVisibleWidth = dipToPixel(MINIMUM_VISIBLE_WIDTH_IN_DP, mDisplayMetrics);
+        mMinVisibleHeight = dipToPixel(MINIMUM_VISIBLE_HEIGHT_IN_DP, mDisplayMetrics);
 
         mDragEnded = false;
     }
@@ -449,7 +458,8 @@
         }
 
         mDimLayer.setBounds(mTmpRect);
-        mDimLayer.show(getDragLayerLocked(), 0.5f, 0);
+        mDimLayer.show(mService.getDragLayerLocked(), RESIZING_HINT_ALPHA,
+                RESIZING_HINT_DURATION_MS);
     }
 
     @Override /** {@link DimLayer.DimLayerUser} */
@@ -462,9 +472,13 @@
         return mTask.mStack.getDisplayInfo();
     }
 
-    private int getDragLayerLocked() {
-        return mService.mPolicy.windowTypeToLayerLw(WindowManager.LayoutParams.TYPE_DRAG)
-                * WindowManagerService.TYPE_LAYER_MULTIPLIER
-                + WindowManagerService.TYPE_LAYER_OFFSET;
+    @Override
+    public void getBounds(Rect out) {
+        // This dim layer user doesn't need this.
+    }
+
+    @Override
+    public String toShortString() {
+        return TAG;
     }
 }
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 10ea4e2..ca358b1 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -19,13 +19,13 @@
 import static android.app.ActivityManager.*;
 import static com.android.server.wm.WindowManagerService.DEBUG_TASK_MOVEMENT;
 import static com.android.server.wm.WindowManagerService.H.RESIZE_STACK;
+import static com.android.server.wm.WindowManagerService.H.UNUSED;
 import static com.android.server.wm.WindowManagerService.TAG;
 
 import android.annotation.IntDef;
 import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.os.Debug;
-import android.os.RemoteException;
 import android.util.EventLog;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -59,7 +59,7 @@
 
     /** For comparison with DisplayContent bounds. */
     private Rect mTmpRect = new Rect();
-    private Rect TmpRect2 = new Rect();
+    private Rect mTmpRect2 = new Rect();
 
     /** Content limits relative to the DisplayContent this sits in. */
     private Rect mBounds = new Rect();
@@ -176,7 +176,11 @@
             return false;
         }
 
-        mAnimationBackgroundSurface.setBounds(bounds);
+        if (mDisplayContent != null) {
+            mDisplayContent.mDimBehindController.updateDimLayer(this);
+            mAnimationBackgroundSurface.setBounds(bounds);
+        }
+
         mBounds.set(bounds);
         mRotation = rotation;
         return true;
@@ -202,7 +206,8 @@
     }
 
     /** Bounds of the stack with other system factors taken into consideration. */
-    void getBounds(Rect out) {
+    @Override
+    public void getBounds(Rect out) {
         if (useCurrentBounds()) {
             // No need to adjust the output bounds if fullscreen or the docked stack is visible
             // since it is already what we want to represent to the rest of the system.
@@ -226,15 +231,15 @@
             } else if (mFullscreen) {
                 setBounds(null);
             } else {
-                TmpRect2.set(mBounds);
+                mTmpRect2.set(mBounds);
                 mDisplayContent.rotateBounds(
-                        mRotation, mDisplayContent.getDisplayInfo().rotation, TmpRect2);
-                if (setBounds(TmpRect2)) {
+                        mRotation, mDisplayContent.getDisplayInfo().rotation, mTmpRect2);
+                if (setBounds(mTmpRect2)) {
                     // Post message to inform activity manager of the bounds change simulating
                     // a one-way call. We do this to prevent a deadlock between window manager
                     // lock and activity manager lock been held.
                     mService.mH.sendMessage(
-                            mService.mH.obtainMessage(RESIZE_STACK, mStackId, -1, mBounds));
+                            mService.mH.obtainMessage(RESIZE_STACK, mStackId, UNUSED, mBounds));
                 }
             }
         }
@@ -383,14 +388,18 @@
         mAnimationBackgroundSurface = new DimLayer(mService, this, mDisplayContent.getDisplayId());
 
         Rect bounds = null;
-        final boolean dockedStackExists = mService.mStackIdToStack.get(DOCKED_STACK_ID) != null;
-        if (mStackId == DOCKED_STACK_ID || (dockedStackExists
+        final TaskStack dockedStack = mService.mStackIdToStack.get(DOCKED_STACK_ID);
+        if (mStackId == DOCKED_STACK_ID || (dockedStack != null
                 && mStackId >= FIRST_STATIC_STACK_ID && mStackId <= LAST_STATIC_STACK_ID)) {
             // The existence of a docked stack affects the size of any static stack created since
             // the docked stack occupies a dedicated region on screen.
             bounds = new Rect();
             displayContent.getLogicalDisplayRect(mTmpRect);
-            getInitialDockedStackBounds(mTmpRect, bounds, mStackId,
+            mTmpRect2.setEmpty();
+            if (dockedStack != null) {
+                dockedStack.getRawBounds(mTmpRect2);
+            }
+            getInitialDockedStackBounds(mTmpRect, bounds, mStackId, mTmpRect2,
                     mDisplayContent.mDividerControllerLocked.getWidthAdjustment());
         }
 
@@ -400,7 +409,7 @@
             // Attaching a docked stack to the display affects the size of all other static
             // stacks since the docked stack occupies a dedicated region on screen.
             // Resize existing static stacks so they are pushed to the side of the docked stack.
-            resizeNonDockedStacks(!FULLSCREEN);
+            resizeNonDockedStacks(!FULLSCREEN, mBounds);
         }
     }
 
@@ -410,29 +419,49 @@
      * @param displayRect The bounds of the display the docked stack is on.
      * @param outBounds Output bounds that should be used for the stack.
      * @param stackId Id of stack we are calculating the bounds for.
+     * @param dockedBounds Bounds of the docked stack.
      * @param adjustment
      */
-    private static void getInitialDockedStackBounds(Rect displayRect, Rect outBounds, int stackId,
-            int adjustment) {
-        // Docked stack start off occupying half the screen space.
+    private static void getInitialDockedStackBounds(
+            Rect displayRect, Rect outBounds, int stackId, Rect dockedBounds, int adjustment) {
         final boolean dockedStack = stackId == DOCKED_STACK_ID;
         final boolean splitHorizontally = displayRect.width() > displayRect.height();
         final boolean topOrLeftCreateMode =
                 WindowManagerService.sDockedStackCreateMode == DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
-        final boolean placeTopOrLeft = (dockedStack && topOrLeftCreateMode)
-                || (!dockedStack && !topOrLeftCreateMode);
+
         outBounds.set(displayRect);
-        if (placeTopOrLeft) {
-            if (splitHorizontally) {
-                outBounds.right = displayRect.centerX() - adjustment;
+        if (dockedStack) {
+            // The initial bounds of the docked stack when it is created half the screen space and
+            // its bounds can be adjusted after that. The bounds of all other stacks are adjusted
+            // to occupy whatever screen space the docked stack isn't occupying.
+            if (topOrLeftCreateMode) {
+                if (splitHorizontally) {
+                    outBounds.right = displayRect.centerX() - adjustment;
+                } else {
+                    outBounds.bottom = displayRect.centerY() - adjustment;
+                }
             } else {
-                outBounds.bottom = displayRect.centerY() - adjustment;
+                if (splitHorizontally) {
+                    outBounds.left = displayRect.centerX() + adjustment;
+                } else {
+                    outBounds.top = displayRect.centerY() + adjustment;
+                }
+            }
+            return;
+        }
+
+        // Other stacks occupy whatever space is left by the docked stack.
+        if (!topOrLeftCreateMode) {
+            if (splitHorizontally) {
+                outBounds.right = dockedBounds.left - adjustment;
+            } else {
+                outBounds.bottom = dockedBounds.top - adjustment;
             }
         } else {
             if (splitHorizontally) {
-                outBounds.left = displayRect.centerX() + adjustment;
+                outBounds.left = dockedBounds.right + adjustment;
             } else {
-                outBounds.top = displayRect.centerY() + adjustment;
+                outBounds.top = dockedBounds.bottom + adjustment;
             }
         }
     }
@@ -441,11 +470,14 @@
      * based on the presence of a docked stack.
      * @param fullscreen If true the stacks will be resized to fullscreen, else they will be
      *                   resized to the appropriate size based on the presence of a docked stack.
+     * @param dockedBounds Bounds of the docked stack.
      */
-    private void resizeNonDockedStacks(boolean fullscreen) {
-        mDisplayContent.getLogicalDisplayRect(mTmpRect);
+    private void resizeNonDockedStacks(boolean fullscreen, Rect dockedBounds) {
+        // Not using mTmpRect because we are posting the object in a message.
+        final Rect bounds = new Rect();
+        mDisplayContent.getLogicalDisplayRect(bounds);
         if (!fullscreen) {
-            getInitialDockedStackBounds(mTmpRect, mTmpRect, FULLSCREEN_WORKSPACE_STACK_ID,
+            getInitialDockedStackBounds(bounds, bounds, FULLSCREEN_WORKSPACE_STACK_ID, dockedBounds,
                     mDisplayContent.mDividerControllerLocked.getWidth());
         }
 
@@ -456,11 +488,8 @@
             if (otherStackId != DOCKED_STACK_ID
                     && otherStackId >= FIRST_STATIC_STACK_ID
                     && otherStackId <= LAST_STATIC_STACK_ID) {
-                try {
-                    mService.mActivityManager.resizeStack(otherStackId, mTmpRect);
-                } catch (RemoteException e) {
-                    // This will not happen since we are in the same process.
-                }
+                mService.mH.sendMessage(
+                        mService.mH.obtainMessage(RESIZE_STACK, otherStackId, UNUSED, bounds));
             }
         }
     }
@@ -488,7 +517,7 @@
         if (mStackId == DOCKED_STACK_ID) {
             // Docked stack was detached from the display, so we no longer need to restrict the
             // region of the screen other static stacks occupy. Go ahead and make them fullscreen.
-            resizeNonDockedStacks(FULLSCREEN);
+            resizeNonDockedStacks(FULLSCREEN, null);
         }
 
         close();
@@ -527,9 +556,6 @@
             mAnimationBackgroundSurface.destroySurface();
             mAnimationBackgroundSurface = null;
         }
-        for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
-            mTasks.get(taskNdx).close();
-        }
         mDisplayContent = null;
     }
 
@@ -586,6 +612,11 @@
         return "{stackId=" + mStackId + " tasks=" + mTasks + "}";
     }
 
+    @Override
+    public String toShortString() {
+        return "Stack=" + mStackId;
+    }
+
     /**
      * For docked workspace provides information which side of the screen was the dock anchored.
      */
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index ab1bf20..928a117 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -272,25 +272,6 @@
                 winAnimator.mWasAnimating = nowAnimating;
                 mAnimating |= nowAnimating;
 
-                boolean appWindowAnimating = winAnimator.mAppAnimator != null
-                        && winAnimator.mAppAnimator.animating;
-                boolean wasAppWindowAnimating = winAnimator.mAppAnimator != null
-                        && winAnimator.mAppAnimator.wasAnimating;
-                boolean anyAnimating = appWindowAnimating || nowAnimating;
-                boolean anyWasAnimating = wasAppWindowAnimating || wasAnimating;
-
-                try {
-                    if (anyAnimating && !anyWasAnimating) {
-                        win.mClient.onAnimationStarted(winAnimator.mAnimatingMove ? -1
-                                : winAnimator.mKeyguardGoingAwayAnimation ? 1
-                                : 0);
-                    } else if (!anyAnimating && anyWasAnimating) {
-                        win.mClient.onAnimationStopped();
-                    }
-                } catch (RemoteException e) {
-                    Slog.w(TAG, "Failed to dispatch window animation state change.", e);
-                }
-
                 if (WindowManagerService.DEBUG_WALLPAPER) {
                     Slog.v(TAG, win + ": wasAnimating=" + wasAnimating +
                             ", nowAnimating=" + nowAnimating);
@@ -753,6 +734,9 @@
         if (!mAnimating && wasAnimating) {
             mWindowPlacerLocked.requestTraversal();
         }
+
+        mService.destroyPreservedSurfaceLocked();
+
         if (WindowManagerService.DEBUG_WINDOW_TRACE) {
             Slog.i(TAG, "!!! animate: exit mAnimating=" + mAnimating
                 + " mBulkUpdateParams=" + Integer.toHexString(mBulkUpdateParams)
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 74572cf..6239d2e 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -18,6 +18,9 @@
 
 import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
 import static android.app.ActivityManager.DOCKED_STACK_ID;
+import static android.app.ActivityManager.FREEFORM_WORKSPACE_STACK_ID;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
 import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
 import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
 import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
@@ -216,13 +219,13 @@
     static final boolean DEBUG_STACK = false;
     static final boolean DEBUG_DISPLAY = false;
     static final boolean DEBUG_POWER = false;
+    static final boolean DEBUG_DIM_LAYER = false;
     static final boolean SHOW_SURFACE_ALLOC = false;
     static final boolean SHOW_TRANSACTIONS = false;
     static final boolean SHOW_LIGHT_TRANSACTIONS = false || SHOW_TRANSACTIONS;
     static final boolean HIDE_STACK_CRAWLS = true;
     static final int LAYOUT_REPEAT_THRESHOLD = 4;
 
-
     static final boolean PROFILE_ORIENTATION = false;
     static final boolean localLOGV = DEBUG;
 
@@ -397,6 +400,13 @@
     final ArrayList<WindowState> mDestroySurface = new ArrayList<>();
 
     /**
+     * Windows with a preserved surface waiting to be destroyed. These windows
+     * are going through a surface change. We keep the old surface around until
+     * the first frame on the new surface finishes drawing.
+     */
+    final ArrayList<WindowState> mDestroyPreservedSurface = new ArrayList<>();
+
+    /**
      * Windows that have lost input focus and are waiting for the new
      * focus window to be displayed before they are told about this.
      */
@@ -466,6 +476,11 @@
 
     static int sDockedStackCreateMode = DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
 
+    int getDragLayerLocked() {
+        return mPolicy.windowTypeToLayerLw(LayoutParams.TYPE_DRAG) * TYPE_LAYER_MULTIPLIER
+                + TYPE_LAYER_OFFSET;
+    }
+
     class RotationWatcher {
         IRotationWatcher watcher;
         IBinder.DeathRecipient deathRecipient;
@@ -1004,7 +1019,7 @@
      * @param displayContent The display we are interested in.
      * @return List of windows from token that are on displayContent.
      */
-    WindowList getTokenWindowsOnDisplay(WindowToken token, DisplayContent displayContent) {
+    private WindowList getTokenWindowsOnDisplay(WindowToken token, DisplayContent displayContent) {
         final WindowList windowList = new WindowList();
         final int count = token.windows.size();
         for (int i = 0; i < count; i++) {
@@ -1038,55 +1053,19 @@
     }
 
     private int addAppWindowToListLocked(final WindowState win) {
-        final IWindow client = win.mClient;
-        final WindowToken token = win.mToken;
         final DisplayContent displayContent = win.getDisplayContent();
         if (displayContent == null) {
             // It doesn't matter this display is going away.
             return 0;
         }
+        final IWindow client = win.mClient;
+        final WindowToken token = win.mToken;
 
-        final WindowList windows = win.getWindowList();
-        final int N = windows.size();
+        final WindowList windows = displayContent.getWindowList();
         WindowList tokenWindowList = getTokenWindowsOnDisplay(token, displayContent);
         int tokenWindowsPos = 0;
-        int windowListPos = tokenWindowList.size();
         if (!tokenWindowList.isEmpty()) {
-            // If this application has existing windows, we
-            // simply place the new window on top of them... but
-            // keep the starting window on top.
-            if (win.mAttrs.type == TYPE_BASE_APPLICATION) {
-                // Base windows go behind everything else.
-                WindowState lowestWindow = tokenWindowList.get(0);
-                placeWindowBefore(lowestWindow, win);
-                tokenWindowsPos = indexOfWinInWindowList(lowestWindow, token.windows);
-            } else {
-                AppWindowToken atoken = win.mAppToken;
-                WindowState lastWindow = tokenWindowList.get(windowListPos - 1);
-                if (atoken != null && lastWindow == atoken.startingWindow) {
-                    placeWindowBefore(lastWindow, win);
-                    tokenWindowsPos = indexOfWinInWindowList(lastWindow, token.windows);
-                } else {
-                    int newIdx = findIdxBasedOnAppTokens(win);
-                    //there is a window above this one associated with the same
-                    //apptoken note that the window could be a floating window
-                    //that was created later or a window at the top of the list of
-                    //windows associated with this token.
-                    if (DEBUG_FOCUS_LIGHT || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG,
-                            "not Base app: Adding window " + win + " at " + (newIdx + 1) + " of " +
-                            N);
-                    windows.add(newIdx + 1, win);
-                    if (newIdx < 0) {
-                        // No window from token found on win's display.
-                        tokenWindowsPos = 0;
-                    } else {
-                        tokenWindowsPos = indexOfWinInWindowList(
-                                windows.get(newIdx), token.windows) + 1;
-                    }
-                    mWindowsChanged = true;
-                }
-            }
-            return tokenWindowsPos;
+            return addAppWindowToTokenListLocked(win, token, windows, tokenWindowList);
         }
 
         // No windows from this token on this display
@@ -1188,19 +1167,65 @@
         // Just search for the start of this layer.
         final int myLayer = win.mBaseLayer;
         int i;
-        for (i = N - 1; i >= 0; --i) {
+        for (i = windows.size() - 1; i >= 0; --i) {
             WindowState w = windows.get(i);
-            if (w.mBaseLayer <= myLayer) {
+            // Dock divider shares the base layer with application windows, but we want to always
+            // keep it above the application windows. The sharing of the base layer is intended
+            // for window animations, which need to be above the dock divider for the duration
+            // of the animation.
+            if (w.mBaseLayer <= myLayer && w.mAttrs.type != TYPE_DOCK_DIVIDER) {
                 break;
             }
         }
         if (DEBUG_FOCUS_LIGHT || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG,
-                "Based on layer: Adding window " + win + " at " + (i + 1) + " of " + N);
+                "Based on layer: Adding window " + win + " at " + (i + 1) + " of "
+                        + windows.size());
         windows.add(i + 1, win);
         mWindowsChanged = true;
         return tokenWindowsPos;
     }
 
+    private int addAppWindowToTokenListLocked(WindowState win, WindowToken token,
+            WindowList windows, WindowList tokenWindowList) {
+        int tokenWindowsPos;
+        // If this application has existing windows, we
+        // simply place the new window on top of them... but
+        // keep the starting window on top.
+        if (win.mAttrs.type == TYPE_BASE_APPLICATION) {
+            // Base windows go behind everything else.
+            WindowState lowestWindow = tokenWindowList.get(0);
+            placeWindowBefore(lowestWindow, win);
+            tokenWindowsPos = indexOfWinInWindowList(lowestWindow, token.windows);
+        } else {
+            AppWindowToken atoken = win.mAppToken;
+            final int windowListPos = tokenWindowList.size();
+            WindowState lastWindow = tokenWindowList.get(windowListPos - 1);
+            if (atoken != null && lastWindow == atoken.startingWindow) {
+                placeWindowBefore(lastWindow, win);
+                tokenWindowsPos = indexOfWinInWindowList(lastWindow, token.windows);
+            } else {
+                int newIdx = findIdxBasedOnAppTokens(win);
+                //there is a window above this one associated with the same
+                //apptoken note that the window could be a floating window
+                //that was created later or a window at the top of the list of
+                //windows associated with this token.
+                if (DEBUG_FOCUS_LIGHT || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG,
+                        "not Base app: Adding window " + win + " at " + (newIdx + 1) + " of "
+                                + windows.size());
+                windows.add(newIdx + 1, win);
+                if (newIdx < 0) {
+                    // No window from token found on win's display.
+                    tokenWindowsPos = 0;
+                } else {
+                    tokenWindowsPos = indexOfWinInWindowList(
+                            windows.get(newIdx), token.windows) + 1;
+                }
+                mWindowsChanged = true;
+            }
+        }
+        return tokenWindowsPos;
+    }
+
     private void addFreeWindowToListLocked(final WindowState win) {
         final WindowList windows = win.getWindowList();
 
@@ -1310,13 +1335,18 @@
     static boolean canBeImeTarget(WindowState w) {
         final int fl = w.mAttrs.flags
                 & (FLAG_NOT_FOCUSABLE|FLAG_ALT_FOCUSABLE_IM);
+        final int type = w.mAttrs.type;
+        // The dock divider has to sit above the application windows and so does the IME. IME also
+        // needs to sit above the dock divider, so it doesn't get cut in half. We make the dock
+        // divider be a target for IME, so this relationship can occur naturally.
         if (fl == 0 || fl == (FLAG_NOT_FOCUSABLE|FLAG_ALT_FOCUSABLE_IM)
-                || w.mAttrs.type == TYPE_APPLICATION_STARTING) {
+                || type == TYPE_APPLICATION_STARTING || type == TYPE_DOCK_DIVIDER) {
             if (DEBUG_INPUT_METHOD) {
                 Slog.i(TAG, "isVisibleOrAdding " + w + ": " + w.isVisibleOrAdding());
                 if (!w.isVisibleOrAdding()) {
                     Slog.i(TAG, "  mSurface=" + w.mWinAnimator.mSurfaceControl
-                            + " relayoutCalled=" + w.mRelayoutCalled + " viewVis=" + w.mViewVisibility
+                            + " relayoutCalled=" + w.mRelayoutCalled
+                            + " viewVis=" + w.mViewVisibility
                             + " policyVis=" + w.mPolicyVisibility
                             + " policyVisAfterAnim=" + w.mPolicyVisibilityAfterAnim
                             + " attachHid=" + w.mAttachedHidden
@@ -2441,7 +2471,6 @@
         boolean configChanged;
         boolean surfaceChanged = false;
         boolean dragResizing = false;
-        boolean animating;
         boolean hasStatusBarPermission =
                 mContext.checkCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR)
                         == PackageManager.PERMISSION_GRANTED;
@@ -2592,16 +2621,10 @@
 
                 // If we're starting a drag-resize, we'll be changing the surface size as well as
                 // notifying the client to render to with an offset from the surface's top-left.
-                // Do a screen freeze, and keep the old surface until the the first frame drawn to
-                // the new surface comes back, so that we avoid a flash due to mismatching surface
-                // setups on window manager side and client side.
                 if (win.isDragResizeChanged()) {
                     win.setDragResizing();
                     if (win.mHasSurface) {
-                        winAnimator.mDestroyPendingSurfaceUponRedraw = true;
-                        winAnimator.mSurfaceDestroyDeferred = true;
-                        winAnimator.destroySurfaceLocked();
-                        startFreezingDisplayLocked(false, 0, 0);
+                        winAnimator.preserveSurfaceLocked();
                         toBeDisplayed = true;
                     }
                 }
@@ -3179,9 +3202,9 @@
 
     public int getOrientationLocked() {
         if (mDisplayFrozen) {
-            if (mLastWindowForcedOrientation != ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) {
-                if (DEBUG_ORIENTATION) Slog.v(TAG, "Display is frozen, return "
-                        + mLastWindowForcedOrientation);
+            if (mLastWindowForcedOrientation != SCREEN_ORIENTATION_UNSPECIFIED) {
+                if (DEBUG_ORIENTATION) Slog.v(TAG,
+                        "Display is frozen, return " + mLastWindowForcedOrientation);
                 // If the display is frozen, some activities may be in the middle
                 // of restarting, and thus have removed their old window.  If the
                 // window has the flag to hide the lock screen, then the lock screen
@@ -3203,8 +3226,7 @@
                     continue;
                 }
                 int req = win.mAttrs.screenOrientation;
-                if((req == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) ||
-                        (req == ActivityInfo.SCREEN_ORIENTATION_BEHIND)){
+                if(req == SCREEN_ORIENTATION_UNSPECIFIED || req == SCREEN_ORIENTATION_BEHIND) {
                     continue;
                 }
 
@@ -3214,7 +3236,7 @@
                 }
                 return (mLastWindowForcedOrientation = req);
             }
-            mLastWindowForcedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+            mLastWindowForcedOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
 
             if (mPolicy.isKeyguardLocked()) {
                 // The screen is locked and no top system window is requesting an orientation.
@@ -3225,7 +3247,7 @@
                         null : winShowWhenLocked.mAppToken;
                 if (appShowWhenLocked != null) {
                     int req = appShowWhenLocked.requestedOrientation;
-                    if (req == ActivityInfo.SCREEN_ORIENTATION_BEHIND) {
+                    if (req == SCREEN_ORIENTATION_BEHIND) {
                         req = mLastKeyguardForcedOrientation;
                     }
                     if (DEBUG_ORIENTATION) Slog.v(TAG, "Done at " + appShowWhenLocked
@@ -3238,8 +3260,21 @@
             }
         }
 
+        final TaskStack dockedStack = mStackIdToStack.get(DOCKED_STACK_ID);
+        final TaskStack freeformStack = mStackIdToStack.get(FREEFORM_WORKSPACE_STACK_ID);
+        if ((dockedStack != null && dockedStack.isVisibleLocked())
+                || (freeformStack != null && freeformStack.isVisibleLocked())) {
+            // We don't let app affect the system orientation when in freeform or docked mode since
+            // they don't occupy the entire display and their request can conflict with other apps.
+            return SCREEN_ORIENTATION_UNSPECIFIED;
+        }
+
         // Top system windows are not requesting an orientation. Start searching from apps.
-        int lastOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+        return getAppSpecifiedOrientation();
+    }
+
+    private int getAppSpecifiedOrientation() {
+        int lastOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
         boolean findingBehind = false;
         boolean lastFullscreen = false;
         // TODO: Multi window.
@@ -3256,19 +3291,16 @@
                 // if we're about to tear down this window and not seek for
                 // the behind activity, don't use it for orientation
                 if (!findingBehind && !atoken.hidden && atoken.hiddenRequested) {
-                    if (DEBUG_ORIENTATION) Slog.v(TAG, "Skipping " + atoken
-                            + " -- going to hide");
+                    if (DEBUG_ORIENTATION) Slog.v(TAG,
+                            "Skipping " + atoken + " -- going to hide");
                     continue;
                 }
 
                 if (tokenNdx == firstToken) {
-                    // If we have hit a new Task, and the bottom
-                    // of the previous group didn't explicitly say to use
-                    // the orientation behind it, and the last app was
-                    // full screen, then we'll stick with the
-                    // user's orientation.
-                    if (lastOrientation != ActivityInfo.SCREEN_ORIENTATION_BEHIND
-                            && lastFullscreen) {
+                    // If we have hit a new Task, and the bottom of the previous group didn't
+                    // explicitly say to use the orientation behind it, and the last app was
+                    // full screen, then we'll stick with the user's orientation.
+                    if (lastOrientation != SCREEN_ORIENTATION_BEHIND && lastFullscreen) {
                         if (DEBUG_ORIENTATION) Slog.v(TAG, "Done at " + atoken
                                 + " -- end of group, return " + lastOrientation);
                         return lastOrientation;
@@ -3277,8 +3309,8 @@
 
                 // We ignore any hidden applications on the top.
                 if (atoken.hiddenRequested) {
-                    if (DEBUG_ORIENTATION) Slog.v(TAG, "Skipping " + atoken
-                            + " -- hidden on top");
+                    if (DEBUG_ORIENTATION) Slog.v(TAG,
+                            "Skipping " + atoken + " -- hidden on top");
                     continue;
                 }
 
@@ -3292,23 +3324,22 @@
                 // to use the orientation behind it, then just take whatever
                 // orientation it has and ignores whatever is under it.
                 lastFullscreen = atoken.appFullscreen;
-                if (lastFullscreen && or != ActivityInfo.SCREEN_ORIENTATION_BEHIND) {
-                    if (DEBUG_ORIENTATION) Slog.v(TAG, "Done at " + atoken
-                            + " -- full screen, return " + or);
+                if (lastFullscreen && or != SCREEN_ORIENTATION_BEHIND) {
+                    if (DEBUG_ORIENTATION) Slog.v(TAG,
+                            "Done at " + atoken + " -- full screen, return " + or);
                     return or;
                 }
                 // If this application has requested an explicit orientation, then use it.
-                if (or != ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
-                        && or != ActivityInfo.SCREEN_ORIENTATION_BEHIND) {
-                    if (DEBUG_ORIENTATION) Slog.v(TAG, "Done at " + atoken
-                            + " -- explicitly set, return " + or);
+                if (or != SCREEN_ORIENTATION_UNSPECIFIED && or != SCREEN_ORIENTATION_BEHIND) {
+                    if (DEBUG_ORIENTATION) Slog.v(TAG,
+                            "Done at " + atoken + " -- explicitly set, return " + or);
                     return or;
                 }
-                findingBehind |= (or == ActivityInfo.SCREEN_ORIENTATION_BEHIND);
+                findingBehind |= (or == SCREEN_ORIENTATION_BEHIND);
             }
         }
-        if (DEBUG_ORIENTATION) Slog.v(TAG, "No app is requesting an orientation, return "
-                + mForcedAppOrientation);
+        if (DEBUG_ORIENTATION) Slog.v(TAG,
+                "No app is requesting an orientation, return " + mForcedAppOrientation);
         // The next app has not been requested to be visible, so we keep the current orientation
         // to prevent freezing/unfreezing the display too early.
         return mForcedAppOrientation;
@@ -3411,7 +3442,6 @@
         }
     }
 
-    @Override
     public void setNewConfiguration(Configuration config) {
         if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
                 "setNewConfiguration()")) {
@@ -3419,12 +3449,20 @@
         }
 
         synchronized(mWindowMap) {
+            final boolean orientationChanged = mCurConfiguration.orientation != config.orientation;
             mCurConfiguration = new Configuration(config);
             if (mWaitingForConfig) {
                 mWaitingForConfig = false;
                 mLastFinishedFreezeSource = "new-config";
             }
             mWindowPlacerLocked.performSurfacePlacement();
+            if (orientationChanged) {
+                for (int i = mDisplayContents.size() - 1; i >= 0; i--) {
+                    DisplayContent content = mDisplayContents.valueAt(i);
+                    Message.obtain(mH, H.UPDATE_DOCKED_STACK_DIVIDER, H.DOCK_DIVIDER_FORCE_UPDATE,
+                            H.UNUSED, content).sendToTarget();
+                }
+            }
         }
     }
 
@@ -5558,10 +5596,13 @@
         int retryCount = 0;
         WindowState appWin = null;
 
-        final boolean appIsImTarget = mInputMethodTarget != null
-                && mInputMethodTarget.mAppToken != null
-                && mInputMethodTarget.mAppToken.appToken != null
-                && mInputMethodTarget.mAppToken.appToken.asBinder() == appToken;
+        boolean appIsImTarget;
+        synchronized(mWindowMap) {
+            appIsImTarget = mInputMethodTarget != null
+                    && mInputMethodTarget.mAppToken != null
+                    && mInputMethodTarget.mAppToken.appToken != null
+                    && mInputMethodTarget.mAppToken.appToken.asBinder() == appToken;
+        }
 
         final int aboveAppLayer = (mPolicy.windowTypeToLayerLw(TYPE_APPLICATION) + 1)
                 * TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET;
@@ -7164,6 +7205,21 @@
         public static final int RESIZE_STACK = 43;
         public static final int RESIZE_TASK = 44;
 
+        /**
+         * Used to indicate in the message that the dock divider needs to be updated only if it's
+         * necessary.
+         */
+        static final int DOCK_DIVIDER_NO_FORCE_UPDATE = 0;
+        /**
+         * Used to indicate in the message that the dock divider should be force-removed before
+         * updating, so new configuration can be applied.
+         */
+        static final int DOCK_DIVIDER_FORCE_UPDATE = 1;
+        /**
+         * Used to denote that an integer field in a message will not be used.
+         */
+        public static final int UNUSED = 0;
+
         @Override
         public void handleMessage(Message msg) {
             if (DEBUG_WINDOW_TRACE) {
@@ -7705,8 +7761,9 @@
                 break;
                 case UPDATE_DOCKED_STACK_DIVIDER: {
                     DisplayContent content = (DisplayContent) msg.obj;
+                    final boolean forceUpdate = msg.arg1 == DOCK_DIVIDER_FORCE_UPDATE;
                     synchronized (mWindowMap) {
-                        content.mDividerControllerLocked.update();
+                        content.mDividerControllerLocked.update(mCurConfiguration, forceUpdate);
                     }
                 }
                 break;
@@ -7733,6 +7790,13 @@
         }
     }
 
+    void destroyPreservedSurfaceLocked() {
+        for (int i = mDestroyPreservedSurface.size() - 1; i >= 0 ; i--) {
+            final WindowState w = mDestroyPreservedSurface.get(i);
+            w.mWinAnimator.destroyPreservedSurfaceLocked();
+        }
+        mDestroyPreservedSurface.clear();
+    }
     // -------------------------------------------------------------
     // IWindowManager API
     // -------------------------------------------------------------
@@ -8356,8 +8420,10 @@
                 layerChanged = true;
                 anyLayerChanged = true;
             }
-            final Task task = w.getTask();
-            if (layerChanged && task != null && task.isDimming(winAnimator)) {
+            final DimLayer.DimLayerUser dimLayerUser = w.getDimLayerUser();
+            final DisplayContent displayContent = w.getDisplayContent();
+            if (layerChanged && dimLayerUser != null && displayContent != null &&
+                    displayContent.mDimBehindController.isDimming(dimLayerUser, winAnimator)) {
                 // Force an animation pass just to update the mDimLayer layer.
                 scheduleAnimationLocked();
             }
@@ -9114,7 +9180,7 @@
         synchronized (mWindowMap) {
             int visibility = mPolicy.adjustSystemUiVisibilityLw(mLastStatusBarVisibility);
             updateStatusBarVisibilityLocked(visibility);
-            mWindowPlacerLocked.performSurfacePlacement();
+            mWindowPlacerLocked.requestTraversal();
         }
     }
 
@@ -10082,5 +10148,12 @@
             }
         }
 
+        @Override
+        public boolean isStackVisible(int stackId) {
+            synchronized (mWindowMap) {
+                final TaskStack stack = mStackIdToStack.get(stackId);
+                return (stack != null && stack.isVisibleLocked());
+            }
+        }
     }
 }
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 55ddbc0..383ad8c 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -29,6 +29,7 @@
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
 import static com.android.server.wm.WindowManagerService.DEBUG_CONFIGURATION;
+import static com.android.server.wm.WindowManagerService.DEBUG_DIM_LAYER;
 import static com.android.server.wm.WindowManagerService.DEBUG_LAYOUT;
 import static com.android.server.wm.WindowManagerService.DEBUG_ORIENTATION;
 import static com.android.server.wm.WindowManagerService.DEBUG_POWER;
@@ -582,12 +583,6 @@
                 if (mContainingFrame.isEmpty()) {
                     mContainingFrame.set(cf);
                 }
-            } else {
-                // Make sure the containing frame is within the content frame so we don't layout
-                // resized window under screen decorations.
-                if (!mContainingFrame.intersect(cf)) {
-                    mContainingFrame.set(cf);
-                }
             }
             mDisplayFrame.set(mContainingFrame);
         } else {
@@ -698,10 +693,10 @@
             final int height = Math.min(mFrame.height(), mContentFrame.height());
             final int width = Math.min(mContentFrame.width(), mFrame.width());
             final DisplayMetrics displayMetrics = getDisplayContent().getDisplayMetrics();
-            final int minVisibleHeight =
-                    mService.dipToPixel(MINIMUM_VISIBLE_HEIGHT_IN_DP, displayMetrics);
-            final int minVisibleWidth =
-                    mService.dipToPixel(MINIMUM_VISIBLE_WIDTH_IN_DP, displayMetrics);
+            final int minVisibleHeight = WindowManagerService.dipToPixel(
+                    MINIMUM_VISIBLE_HEIGHT_IN_DP, displayMetrics);
+            final int minVisibleWidth = WindowManagerService.dipToPixel(
+                    MINIMUM_VISIBLE_WIDTH_IN_DP, displayMetrics);
             final int top = Math.max(mContentFrame.top,
                     Math.min(mFrame.top, mContentFrame.bottom - minVisibleHeight));
             final int left = Math.max(mContentFrame.left + minVisibleWidth - width,
@@ -712,6 +707,7 @@
             mStableFrame.set(mContentFrame);
         } else if (mAttrs.type == TYPE_DOCK_DIVIDER) {
             mDisplayContent.mDividerControllerLocked.positionDockedStackedDivider(mFrame);
+            mContentFrame.set(mFrame);
         } else {
             mContentFrame.set(Math.max(mContentFrame.left, mFrame.left),
                     Math.max(mContentFrame.top, mFrame.top),
@@ -889,7 +885,7 @@
 
     @Override
     public boolean isVoiceInteraction() {
-        return mAppToken != null ? mAppToken.voiceInteraction : false;
+        return mAppToken != null && mAppToken.voiceInteraction;
     }
 
     boolean setInsetsChanged() {
@@ -934,7 +930,10 @@
                 return task.mStack;
             }
         }
-        return null;
+        // Some system windows (e.g. "Power off" dialog) don't have a task, but we would still
+        // associate them with some stack to enable dimming.
+        return mAttrs.type >= WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW
+                && mDisplayContent != null ? mDisplayContent.getHomeStack() : null;
     }
 
     /**
@@ -970,7 +969,8 @@
         }
         if (forTouch && inFreeformWorkspace()) {
             final DisplayMetrics displayMetrics = getDisplayContent().getDisplayMetrics();
-            final int delta = mService.dipToPixel(RESIZE_HANDLE_WIDTH_IN_DP, displayMetrics);
+            final int delta = WindowManagerService.dipToPixel(
+                    RESIZE_HANDLE_WIDTH_IN_DP, displayMetrics);
             bounds.inset(-delta, -delta);
         }
     }
@@ -1031,8 +1031,7 @@
             return false;
         }
         final AppWindowToken atoken = mAppToken;
-        final boolean animating = atoken != null
-                ? (atoken.mAppAnimator.animation != null) : false;
+        final boolean animating = atoken != null && atoken.mAppAnimator.animation != null;
         return mHasSurface && !mDestroying && !mExiting
                 && (atoken == null ? mPolicyVisibility : !atoken.hiddenRequested)
                 && ((!mAttachedHidden && mViewVisibility == View.VISIBLE
@@ -1114,8 +1113,7 @@
      * of a transition that has not yet been started.
      */
     boolean isReadyForDisplay() {
-        if (mRootToken.waitingToShow &&
-                mService.mAppTransition.isTransitionSet()) {
+        if (mRootToken.waitingToShow && mService.mAppTransition.isTransitionSet()) {
             return false;
         }
         return mHasSurface && mPolicyVisibility && !mDestroying
@@ -1295,19 +1293,20 @@
     }
 
     void handleFlagDimBehind() {
-        if ((mAttrs.flags & FLAG_DIM_BEHIND) != 0 && isDisplayedLw() && !mExiting) {
-            final Task task = getTask();
-            if (task == null) {
-                return;
-            }
-            task.setContinueDimming();
-            if (!task.isDimming(mWinAnimator)) {
-                if (WindowManagerService.localLOGV) Slog.v(TAG, "Win " + this + " start dimming.");
-                task.startDimmingIfNeeded(mWinAnimator);
-            }
+        if ((mAttrs.flags & FLAG_DIM_BEHIND) != 0 && mDisplayContent != null && !mExiting
+                && isDisplayedLw()) {
+            mDisplayContent.mDimBehindController.applyDimBehind(getDimLayerUser(), mWinAnimator);
         }
     }
 
+    DimLayer.DimLayerUser getDimLayerUser() {
+        Task task = getTask();
+        if (task != null) {
+            return task;
+        }
+        return getStack();
+    }
+
     void maybeRemoveReplacedWindow() {
         AppWindowToken token = mAppToken;
         if (token != null && token.mReplacingWindow) {
@@ -1512,11 +1511,9 @@
 
     @Override
     public boolean isDimming() {
-        Task task = getTask();
-        if (task == null) {
-            return false;
-        }
-        return task.isDimming(mWinAnimator);
+        final DimLayer.DimLayerUser dimLayerUser = getDimLayerUser();
+        return dimLayerUser != null && mDisplayContent != null &&
+                mDisplayContent.mDimBehindController.isDimming(dimLayerUser, mWinAnimator);
     }
 
     public void setShowToOwnerOnlyLocked(boolean showToOwnerOnly) {
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 60bf571..2733933 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -23,11 +23,12 @@
 import static com.android.server.wm.WindowManagerService.DEBUG_ORIENTATION;
 import static com.android.server.wm.WindowManagerService.DEBUG_STARTING_WINDOW;
 import static com.android.server.wm.WindowManagerService.DEBUG_SURFACE_TRACE;
-import static com.android.server.wm.WindowManagerService.SHOW_TRANSACTIONS;
 import static com.android.server.wm.WindowManagerService.DEBUG_VISIBILITY;
+import static com.android.server.wm.WindowManagerService.localLOGV;
 import static com.android.server.wm.WindowManagerService.SHOW_LIGHT_TRANSACTIONS;
 import static com.android.server.wm.WindowManagerService.SHOW_SURFACE_ALLOC;
-import static com.android.server.wm.WindowManagerService.localLOGV;
+import static com.android.server.wm.WindowManagerService.SHOW_TRANSACTIONS;
+import static com.android.server.wm.WindowManagerService.TYPE_LAYER_MULTIPLIER;
 import static com.android.server.wm.WindowSurfacePlacer.SET_ORIENTATION_CHANGE_COMPLETE;
 import static com.android.server.wm.WindowSurfacePlacer.SET_TURN_ON_SCREEN;
 
@@ -66,6 +67,7 @@
  **/
 class WindowStateAnimator {
     static final String TAG = "WindowStateAnimator";
+    static final int WINDOW_FREEZE_LAYER = TYPE_LAYER_MULTIPLIER * 200;
 
     // Unchanging local convenience fields.
     final WindowManagerService mService;
@@ -108,7 +110,7 @@
      */
     boolean mSurfaceDestroyDeferred;
 
-    boolean mDestroyPendingSurfaceUponRedraw;
+    boolean mDestroyPreservedSurfaceUponRedraw;
     float mShownAlpha = 0;
     float mAlpha = 0;
     float mLastAlpha = 0;
@@ -290,6 +292,9 @@
     // This must be called while inside a transaction.  Returns true if
     // there is more animation to run.
     boolean stepAnimationLocked(long currentTime) {
+        // Save the animation state as it was before this step so WindowManagerService can tell if
+        // we just started or just stopped animating by comparing mWasAnimating with isAnimating().
+        mWasAnimating = mAnimating;
         final DisplayContent displayContent = mWin.getDisplayContent();
         if (displayContent != null && mService.okToDisplay()) {
             // We will run animations as long as the display isn't frozen.
@@ -553,12 +558,10 @@
         if (atoken == null || atoken.allDrawn || mWin.mAttrs.type == TYPE_APPLICATION_STARTING) {
             result = performShowLocked();
         }
-        if (mDestroyPendingSurfaceUponRedraw) {
-            mDestroyPendingSurfaceUponRedraw = false;
-            destroyDeferredSurfaceLocked();
-            mService.stopFreezingDisplayLocked();
+        if (mDestroyPreservedSurfaceUponRedraw && result) {
+            mService.mDestroyPreservedSurface.add(mWin);
         }
-        return false;
+        return result;
     }
 
     static class SurfaceTrace extends SurfaceControl {
@@ -778,6 +781,31 @@
         }
     }
 
+    void preserveSurfaceLocked() {
+        if (mDestroyPreservedSurfaceUponRedraw) {
+            return;
+        }
+        if (mSurfaceControl != null) {
+            SurfaceControl.openTransaction();
+            try {
+                mSurfaceControl.setLayer(WINDOW_FREEZE_LAYER);
+            } finally {
+                SurfaceControl.closeTransaction();
+            }
+        }
+        mDestroyPreservedSurfaceUponRedraw = true;
+        mSurfaceDestroyDeferred = true;
+        destroySurfaceLocked();
+    }
+
+    void destroyPreservedSurfaceLocked() {
+        if (!mDestroyPreservedSurfaceUponRedraw) {
+            return;
+        }
+        destroyDeferredSurfaceLocked();
+        mDestroyPreservedSurfaceUponRedraw = false;
+    }
+
     SurfaceControl createSurfaceLocked() {
         final WindowState w = mWin;
         if (mSurfaceControl == null) {
@@ -1516,12 +1544,7 @@
                         mDsDy * w.mHScale, mDtDy * w.mVScale);
                 mAnimator.setPendingLayoutChanges(w.getDisplayId(),
                         WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER);
-                if ((w.mAttrs.flags & LayoutParams.FLAG_DIM_BEHIND) != 0) {
-                    final Task task = w.getTask();
-                    if (task != null) {
-                        task.startDimmingIfNeeded(this);
-                    }
-                }
+                w.handleFlagDimBehind();
             } catch (RuntimeException e) {
                 // If something goes wrong with the surface (such
                 // as running out of memory), don't take down the
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index df0a1c9..112646a 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -642,9 +642,7 @@
                     handleNotObscuredLocked(w, innerDw, innerDh);
                 }
 
-                if (task != null && !task.getContinueDimming()) {
-                    w.handleFlagDimBehind();
-                }
+                w.handleFlagDimBehind();
 
                 if (isDefaultDisplay && obscuredChanged
                         && mWallpaperControllerLocked.isWallpaperTarget(w) && w.isVisibleLw()) {
@@ -967,7 +965,8 @@
         }
 
         mService.mPolicy.finishLayoutLw();
-        mService.mH.obtainMessage(UPDATE_DOCKED_STACK_DIVIDER, displayContent).sendToTarget();
+        mService.mH.obtainMessage(UPDATE_DOCKED_STACK_DIVIDER,
+                DOCK_DIVIDER_NO_FORCE_UPDATE, UNUSED, displayContent).sendToTarget();
     }
 
     /**
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 2dd7cde..0860f02 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -124,6 +124,7 @@
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
 import com.android.server.devicepolicy.DevicePolicyManagerService.ActiveAdmin.TrustAgentInfo;
+import com.android.server.pm.UserRestrictionsUtils;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -277,7 +278,8 @@
     final LocalService mLocalService;
 
     // Stores and loads state on device and profile owners.
-    private final Owners mOwners;
+    @VisibleForTesting
+    final Owners mOwners;
 
     private final Binder mToken = new Binder();
 
@@ -433,6 +435,8 @@
         private static final String TAG_PROVIDER = "provider";
         private static final String TAG_PACKAGE_LIST_ITEM  = "item";
 
+        private static final String TAG_USER_RESTRICTIONS = "user-restrictions";
+
         final DeviceAdminInfo info;
 
         int passwordQuality = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
@@ -512,6 +516,8 @@
 
         List<String> crossProfileWidgetProviders;
 
+        Bundle userRestrictions;
+
         ActiveAdmin(DeviceAdminInfo _info) {
             info = _info;
         }
@@ -686,6 +692,10 @@
             writePackageListToXml(out, TAG_PERMITTED_ACCESSIBILITY_SERVICES,
                     permittedAccessiblityServices);
             writePackageListToXml(out, TAG_PERMITTED_IMES, permittedInputMethods);
+            if (hasUserRestrictions()) {
+                UserRestrictionsUtils.writeRestrictions(
+                        out, userRestrictions, TAG_USER_RESTRICTIONS);
+            }
         }
 
         void writePackageListToXml(XmlSerializer out, String outerTag,
@@ -795,6 +805,8 @@
                     permittedAccessiblityServices = readPackageList(parser, tag);
                 } else if (TAG_PERMITTED_IMES.equals(tag)) {
                     permittedInputMethods = readPackageList(parser, tag);
+                } else if (TAG_USER_RESTRICTIONS.equals(tag)) {
+                    UserRestrictionsUtils.readRestrictions(parser, ensureUserRestrictions());
                 } else {
                     Slog.w(LOG_TAG, "Unknown admin tag: " + tag);
                     XmlUtils.skipCurrentTag(parser);
@@ -915,6 +927,17 @@
             return result;
         }
 
+        boolean hasUserRestrictions() {
+            return userRestrictions != null && userRestrictions.size() > 0;
+        }
+
+        Bundle ensureUserRestrictions() {
+            if (userRestrictions == null) {
+                userRestrictions = new Bundle();
+            }
+            return userRestrictions;
+        }
+
         void dump(String prefix, PrintWriter pw) {
             pw.print(prefix); pw.print("uid="); pw.println(getUid());
             pw.print(prefix); pw.println("policies:");
@@ -984,6 +1007,8 @@
                 pw.print(prefix); pw.print("permittedInputMethods=");
                         pw.println(permittedInputMethods.toString());
             }
+            pw.print(prefix); pw.println("userRestrictions:");
+            UserRestrictionsUtils.dumpRestrictions(pw, prefix + "  ", userRestrictions);
         }
     }
 
@@ -1116,6 +1141,10 @@
             return getCallingUid() == Process.myUid();
         }
 
+        final int userHandleGetCallingUserId() {
+            return UserHandle.getUserId(binderGetCallingUid());
+        }
+
         File environmentGetUserSystemDirectory(int userId) {
             return Environment.getUserSystemDirectory(userId);
         }
@@ -1151,6 +1180,42 @@
         String getDevicePolicyFilePathForSystemUser() {
             return "/data/system/";
         }
+
+        int settingsSecureGetIntForUser(String name, int def, int userHandle) {
+            return Settings.Secure.getIntForUser(mContext.getContentResolver(),
+                    name, def, userHandle);
+        }
+
+        void settingsSecurePutIntForUser(String name, int value, int userHandle) {
+            Settings.Secure.putIntForUser(mContext.getContentResolver(),
+                    name, value, userHandle);
+        }
+
+        void settingsSecurePutStringForUser(String name, String value, int userHandle) {
+            Settings.Secure.putStringForUser(mContext.getContentResolver(),
+                    name, value, userHandle);
+        }
+
+        void settingsGlobalPutStringForUser(String name, String value, int userHandle) {
+            Settings.Global.putStringForUser(mContext.getContentResolver(),
+                    name, value, userHandle);
+        }
+
+        void settingsSecurePutInt(String name, int value) {
+            Settings.Secure.putInt(mContext.getContentResolver(), name, value);
+        }
+
+        void settingsGlobalPutInt(String name, int value) {
+            Settings.Global.putInt(mContext.getContentResolver(), name, value);
+        }
+
+        void settingsSecurePutString(String name, String value) {
+            Settings.Secure.putString(mContext.getContentResolver(), name, value);
+        }
+
+        void settingsGlobalPutString(String name, String value) {
+            Settings.Global.putString(mContext.getContentResolver(), name, value);
+        }
     }
 
     /**
@@ -1261,10 +1326,59 @@
     void loadOwners() {
         synchronized (this) {
             mOwners.load();
+            findOwnerComponentIfNecessaryLocked();
+
+            // TODO PO may not have a class name either due to b/17652534.  Address that too.
+
             updateDeviceOwnerLocked();
         }
     }
 
+    private void findOwnerComponentIfNecessaryLocked() {
+        if (!mOwners.hasDeviceOwner()) {
+            return;
+        }
+        final ComponentName doComponentName = mOwners.getDeviceOwnerComponent();
+
+        if (!TextUtils.isEmpty(doComponentName.getClassName())) {
+            return; // Already a full component name.
+        }
+
+        final ComponentName doComponent = findAdminComponentWithPackageLocked(
+                doComponentName.getPackageName(),
+                mOwners.getDeviceOwnerUserId());
+        if (doComponent == null) {
+            Slog.e(LOG_TAG, "Device-owner isn't registered as device-admin");
+        } else {
+            mOwners.setDeviceOwner(
+                    doComponent,
+                    mOwners.getDeviceOwnerName(),
+                    mOwners.getDeviceOwnerUserId());
+            mOwners.writeDeviceOwner();
+        }
+    }
+
+    private ComponentName findAdminComponentWithPackageLocked(String packageName, int userId) {
+        final DevicePolicyData policy = getUserData(userId);
+        final int n = policy.mAdminList.size();
+        ComponentName found = null;
+        int nFound = 0;
+        for (int i = 0; i < n; i++) {
+            final ActiveAdmin admin = policy.mAdminList.get(i);
+            if (packageName.equals(admin.info.getPackageName())) {
+                // Found!
+                if (nFound == 0) {
+                    found = admin.info.getComponent();
+                }
+                nFound++;
+            }
+        }
+        if (nFound > 0) {
+            Slog.w(LOG_TAG, "Multiple DA found; assume the first one is DO.");
+        }
+        return found;
+    }
+
     /**
      * Set an alarm for an upcoming event - expiration warning, expiration, or post-expiration
      * reminders.  Clears alarm if no expirations are configured.
@@ -1375,23 +1489,20 @@
         return null;
     }
 
-    private boolean isActiveAdminWithPolicyForUserLocked(ActiveAdmin admin, int reqPolicy,
+    @VisibleForTesting
+    boolean isActiveAdminWithPolicyForUserLocked(ActiveAdmin admin, int reqPolicy,
             int userId) {
-        boolean ownsDevice = isDeviceOwner(admin.info.getPackageName());
+        boolean ownsDevice = isDeviceOwner(admin.info.getComponent());
         boolean ownsProfile = (getProfileOwner(userId) != null
                 && getProfileOwner(userId).getPackageName()
                     .equals(admin.info.getPackageName()));
-        boolean ownsInitialization = isDeviceInitializer(admin.info.getPackageName())
-                && !hasUserSetupCompleted(userId);
 
         if (reqPolicy == DeviceAdminInfo.USES_POLICY_DEVICE_OWNER) {
-            if ((userId == UserHandle.USER_SYSTEM && (ownsDevice || ownsInitialization))
-                    || (ownsDevice && ownsProfile)) {
+            if ((userId == UserHandle.USER_SYSTEM && ownsDevice) || (ownsDevice && ownsProfile)) {
                 return true;
             }
         } else if (reqPolicy == DeviceAdminInfo.USES_POLICY_PROFILE_OWNER) {
-            if ((userId == UserHandle.USER_SYSTEM && ownsDevice) || ownsProfile
-                    || ownsInitialization) {
+            if ((userId == UserHandle.USER_SYSTEM && ownsDevice) || ownsProfile) {
                 return true;
             }
         } else {
@@ -1798,7 +1909,6 @@
         validatePasswordOwnerLocked(policy);
         syncDeviceCapabilitiesLocked(policy);
         updateMaximumTimeToLockLocked(policy);
-        addDeviceInitializerToLockTaskPackagesLocked(userHandle);
         updateLockTaskPackagesLocked(policy.mLockTaskPackages, userHandle);
         if (policy.mStatusBarDisabled) {
             setStatusBarDisabledInternal(policy.mStatusBarDisabled, userHandle);
@@ -1820,8 +1930,10 @@
     private void updateDeviceOwnerLocked() {
         long ident = mInjector.binderClearCallingIdentity();
         try {
-            mInjector.getIActivityManager()
-                    .updateDeviceOwner(getDeviceOwner());
+            if (getDeviceOwner() != null) {
+                mInjector.getIActivityManager()
+                        .updateDeviceOwner(getDeviceOwner().getPackageName());
+            }
         } catch (RemoteException e) {
             // Not gonna happen.
         } finally {
@@ -1885,7 +1997,8 @@
         }
     }
 
-    public void systemReady(int phase) {
+    @VisibleForTesting
+    void systemReady(int phase) {
         if (!mHasFeature) {
             return;
         }
@@ -2224,7 +2337,7 @@
             }
             if (admin.getUid() != mInjector.binderGetCallingUid()) {
                 // Active device owners must remain active admins.
-                if (isDeviceOwner(adminReceiver.getPackageName())) {
+                if (isDeviceOwner(adminReceiver)) {
                     return;
                 }
                 mContext.enforceCallingOrSelfPermission(
@@ -3076,7 +3189,7 @@
             return false;
         }
 
-        boolean callerIsDeviceOwnerAdmin = isCallerDeviceOwnerOrInitializer(callingUid);
+        boolean callerIsDeviceOwnerAdmin = isCallerDeviceOwner(callingUid);
         boolean doNotAskCredentialsOnBoot =
                 (flags & DevicePolicyManager.RESET_PASSWORD_DO_NOT_ASK_CREDENTIALS_ON_BOOT) != 0;
         if (callerIsDeviceOwnerAdmin && doNotAskCredentialsOnBoot) {
@@ -3163,8 +3276,7 @@
             } else {
                 // Make sure KEEP_SCREEN_ON is disabled, since that
                 // would allow bypassing of the maximum time to lock.
-                Settings.Global.putInt(mContext.getContentResolver(),
-                        Settings.Global.STAY_ON_WHILE_PLUGGED_IN, 0);
+                mInjector.settingsGlobalPutInt(Settings.Global.STAY_ON_WHILE_PLUGGED_IN, 0);
             }
 
             policy.mLastMaximumTimeToLock = timeMs;
@@ -3383,7 +3495,7 @@
         // If there is a profile owner, redirect to that; otherwise query the device owner.
         ComponentName aliasChooser = getProfileOwner(caller.getIdentifier());
         if (aliasChooser == null && caller.isOwner()) {
-            ActiveAdmin deviceOwnerAdmin = getDeviceOwnerAdmin();
+            ActiveAdmin deviceOwnerAdmin = getDeviceOwnerAdminLocked();
             if (deviceOwnerAdmin != null) {
                 aliasChooser = deviceOwnerAdmin.info.getComponent();
             }
@@ -3484,11 +3596,8 @@
             long ident = mInjector.binderClearCallingIdentity();
             try {
                 if ((flags & WIPE_RESET_PROTECTION_DATA) != 0) {
-                    boolean ownsInitialization = isDeviceInitializer(admin.info.getPackageName())
-                            && !hasUserSetupCompleted(userHandle);
                     if (userHandle != UserHandle.USER_SYSTEM
-                            || !(isDeviceOwner(admin.info.getPackageName())
-                                    || ownsInitialization)) {
+                            || !isDeviceOwner(admin.info.getComponent())) {
                         throw new SecurityException(
                                "Only device owner admins can set WIPE_RESET_PROTECTION_DATA");
                     }
@@ -3846,16 +3955,15 @@
             } catch (NumberFormatException e) {}
         }
         exclusionList = exclusionList.trim();
-        ContentResolver res = mContext.getContentResolver();
 
         ProxyInfo proxyProperties = new ProxyInfo(data[0], proxyPort, exclusionList);
         if (!proxyProperties.isValid()) {
             Slog.e(LOG_TAG, "Invalid proxy properties, ignoring: " + proxyProperties.toString());
             return;
         }
-        Settings.Global.putString(res, Settings.Global.GLOBAL_HTTP_PROXY_HOST, data[0]);
-        Settings.Global.putInt(res, Settings.Global.GLOBAL_HTTP_PROXY_PORT, proxyPort);
-        Settings.Global.putString(res, Settings.Global.GLOBAL_HTTP_PROXY_EXCLUSION_LIST,
+        mInjector.settingsGlobalPutString(Settings.Global.GLOBAL_HTTP_PROXY_HOST, data[0]);
+        mInjector.settingsGlobalPutInt(Settings.Global.GLOBAL_HTTP_PROXY_PORT, proxyPort);
+        mInjector.settingsGlobalPutString(Settings.Global.GLOBAL_HTTP_PROXY_EXCLUSION_LIST,
                 exclusionList);
     }
 
@@ -4079,8 +4187,7 @@
         if (required) {
             long ident = mInjector.binderClearCallingIdentity();
             try {
-                Settings.Global.putInt(mContext.getContentResolver(),
-                        Settings.Global.AUTO_TIME, 1 /* AUTO_TIME on */);
+                mInjector.settingsGlobalPutInt(Settings.Global.AUTO_TIME, 1 /* AUTO_TIME on */);
             } finally {
                 mInjector.binderRestoreCallingIdentity(ident);
             }
@@ -4096,7 +4203,7 @@
             return false;
         }
         synchronized (this) {
-            ActiveAdmin deviceOwner = getDeviceOwnerAdmin();
+            ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
             return (deviceOwner != null) ? deviceOwner.requireAutoTime : false;
         }
     }
@@ -4239,13 +4346,13 @@
     }
 
     @Override
-    public boolean setDeviceOwner(String packageName, String ownerName, int userId) {
+    public boolean setDeviceOwner(ComponentName admin, String ownerName, int userId) {
         if (!mHasFeature) {
             return false;
         }
-        if (packageName == null
-                || !isPackageInstalledForUser(packageName, userId)) {
-            throw new IllegalArgumentException("Invalid package name " + packageName
+        if (admin == null
+                || !isPackageInstalledForUser(admin.getPackageName(), userId)) {
+            throw new IllegalArgumentException("Invalid component " + admin
                     + " for device owner");
         }
         synchronized (this) {
@@ -4261,7 +4368,7 @@
                 mInjector.binderRestoreCallingIdentity(ident);
             }
 
-            mOwners.setDeviceOwner(packageName, ownerName, userId);
+            mOwners.setDeviceOwner(admin, ownerName, userId);
             mOwners.writeDeviceOwner();
             updateDeviceOwnerLocked();
             Intent intent = new Intent(DevicePolicyManager.ACTION_DEVICE_OWNER_CHANGED);
@@ -4277,24 +4384,33 @@
         }
     }
 
+    public boolean isDeviceOwner(ComponentName who) {
+        if (!mHasFeature) {
+            return false;
+        }
+        synchronized (this) {
+            return mOwners.hasDeviceOwner() && mOwners.getDeviceOwnerComponent().equals(who);
+        }
+    }
+
     @Override
-    public boolean isDeviceOwner(String packageName) {
+    public boolean isDeviceOwnerPackage(String packageName) {
         if (!mHasFeature) {
             return false;
         }
         synchronized (this) {
             return mOwners.hasDeviceOwner()
-                    && mOwners.getDeviceOwnerPackageName().equals(packageName);
+                    && mOwners.getDeviceOwnerComponent().getPackageName().equals(packageName);
         }
     }
 
     @Override
-    public String getDeviceOwner() {
+    public ComponentName getDeviceOwner() {
         if (!mHasFeature) {
             return null;
         }
         synchronized (this) {
-            return mOwners.getDeviceOwnerPackageName();
+            return mOwners.getDeviceOwnerComponent();
         }
     }
 
@@ -4317,17 +4433,18 @@
     }
 
     // Returns the active device owner or null if there is no device owner.
-    private ActiveAdmin getDeviceOwnerAdmin() {
-        String deviceOwnerPackageName = getDeviceOwner();
-        if (deviceOwnerPackageName == null) {
+    @VisibleForTesting
+    ActiveAdmin getDeviceOwnerAdminLocked() {
+        ComponentName component = getDeviceOwner();
+        if (component == null) {
             return null;
         }
 
-        DevicePolicyData policy = getUserData(UserHandle.USER_SYSTEM);
+        DevicePolicyData policy = getUserData(mOwners.getDeviceOwnerUserId());
         final int n = policy.mAdminList.size();
         for (int i = 0; i < n; i++) {
             ActiveAdmin admin = policy.mAdminList.get(i);
-            if (deviceOwnerPackageName.equals(admin.info.getPackageName())) {
+            if (component.equals(admin.info.getComponent())) {
                 return admin;
             }
         }
@@ -4337,15 +4454,17 @@
     @Override
     public void clearDeviceOwner(String packageName) {
         Preconditions.checkNotNull(packageName, "packageName is null");
+        final int callingUid = mInjector.binderGetCallingUid();
         try {
             int uid = mContext.getPackageManager().getPackageUid(packageName, 0);
-            if (uid != mInjector.binderGetCallingUid()) {
+            if (uid != callingUid) {
                 throw new SecurityException("Invalid packageName");
             }
         } catch (NameNotFoundException e) {
             throw new SecurityException(e);
         }
-        if (!isDeviceOwner(packageName)) {
+        if (!mOwners.hasDeviceOwner() || !getDeviceOwner().getPackageName().equals(packageName)
+                || (mOwners.getDeviceOwnerUserId() != UserHandle.getUserId(callingUid))) {
             throw new SecurityException("clearDeviceOwner can only be called by the device owner");
         }
         synchronized (this) {
@@ -4367,126 +4486,6 @@
     }
 
     @Override
-    public boolean setDeviceInitializer(ComponentName who, ComponentName initializer) {
-        if (!mHasFeature) {
-            return false;
-        }
-        if (initializer == null ||
-                !mOwners.hasDeviceOwner() ||
-                !isPackageInstalledForUser(initializer.getPackageName(),
-                        mOwners.getDeviceOwnerUserId())) {
-            throw new IllegalArgumentException("Invalid component name " + initializer
-                    + " for device initializer or no device owner set");
-        }
-        boolean isInitializerSystemApp;
-        try {
-            isInitializerSystemApp = isSystemApp(mIPackageManager,
-                    initializer.getPackageName(),
-                    mInjector.binderGetCallingUserHandle().getIdentifier());
-        } catch (RemoteException | IllegalArgumentException e) {
-            isInitializerSystemApp = false;
-            Slog.e(LOG_TAG, "Fail to check if device initialzer is system app.", e);
-        }
-        if (!isInitializerSystemApp) {
-            throw new IllegalArgumentException("Only system app can be set as device initializer.");
-        }
-        synchronized (this) {
-            enforceCanSetDeviceInitializer(who);
-
-            if (mOwners.hasDeviceInitializer()) {
-                throw new IllegalStateException(
-                        "Trying to set device initializer but device initializer is already set.");
-            }
-
-            mOwners.setDeviceInitializer(initializer);
-
-            addDeviceInitializerToLockTaskPackagesLocked(mOwners.getDeviceOwnerUserId());
-            mOwners.writeDeviceOwner();
-            return true;
-        }
-    }
-
-    private void enforceCanSetDeviceInitializer(ComponentName who) {
-        if (who == null) {
-            mContext.enforceCallingOrSelfPermission(
-                    android.Manifest.permission.MANAGE_DEVICE_ADMINS, null);
-            if (hasUserSetupCompleted(UserHandle.USER_SYSTEM)) {
-                throw new IllegalStateException(
-                        "Trying to set device initializer but device is already provisioned.");
-            }
-        } else {
-            getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
-        }
-    }
-
-    @Override
-    public boolean isDeviceInitializer(String packageName) {
-        if (!mHasFeature) {
-            return false;
-        }
-        synchronized (this) {
-            return mOwners.hasDeviceInitializer()
-                    && mOwners.getDeviceInitializerPackageName().equals(packageName);
-        }
-    }
-
-    @Override
-    public String getDeviceInitializer() {
-        if (!mHasFeature) {
-            return null;
-        }
-        synchronized (this) {
-            if (mOwners.hasDeviceInitializer()) {
-                return mOwners.getDeviceInitializerPackageName();
-            }
-        }
-        return null;
-    }
-
-    @Override
-    public ComponentName getDeviceInitializerComponent() {
-        if (!mHasFeature) {
-            return null;
-        }
-        synchronized (this) {
-            if (mOwners.hasDeviceInitializer()) {
-                return mOwners.getDeviceInitializerComponent();
-            }
-        }
-        return null;
-    }
-
-    @Override
-    public void clearDeviceInitializer(ComponentName who) {
-        if (!mHasFeature) {
-            return;
-        }
-        Preconditions.checkNotNull(who, "ComponentName is null");
-
-        ActiveAdmin admin = getActiveAdminUncheckedLocked(who, UserHandle.getCallingUserId());
-
-        if (admin.getUid() != mInjector.binderGetCallingUid()) {
-            throw new SecurityException("Admin " + who + " is not owned by uid "
-                    + mInjector.binderGetCallingUid());
-        }
-
-        if (!isDeviceInitializer(admin.info.getPackageName())
-                && !isDeviceOwner(admin.info.getPackageName())) {
-            throw new SecurityException(
-                    "clearDeviceInitializer can only be called by the device initializer/owner");
-        }
-        synchronized (this) {
-            long ident = mInjector.binderClearCallingIdentity();
-            try {
-                mOwners.clearDeviceInitializer();
-                mOwners.writeDeviceOwner();
-            } finally {
-                mInjector.binderRestoreCallingIdentity(ident);
-            }
-        }
-    }
-
-    @Override
     public boolean setProfileOwner(ComponentName who, String ownerName, int userHandle) {
         if (!mHasFeature) {
             return false;
@@ -4576,50 +4575,6 @@
     }
 
     @Override
-    public boolean setUserEnabled(ComponentName who) {
-        if (!mHasFeature) {
-            return false;
-        }
-        synchronized (this) {
-            if (who == null) {
-                throw new NullPointerException("ComponentName is null");
-            }
-            int userId = UserHandle.getCallingUserId();
-
-            ActiveAdmin activeAdmin =
-                    getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
-            if (!isDeviceInitializer(activeAdmin.info.getPackageName())) {
-                throw new SecurityException(
-                        "This method can only be called by device initializers");
-            }
-
-            long id = mInjector.binderClearCallingIdentity();
-            try {
-                if (!isDeviceOwner(activeAdmin.info.getPackageName())) {
-                    mIPackageManager.setComponentEnabledSetting(who,
-                            PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
-                            PackageManager.DONT_KILL_APP, userId);
-
-                    removeActiveAdmin(who, userId);
-                }
-
-                if (userId == UserHandle.USER_SYSTEM) {
-                    Settings.Global.putInt(mContext.getContentResolver(),
-                            Settings.Global.DEVICE_PROVISIONED, 1);
-                }
-                Settings.Secure.putIntForUser(mContext.getContentResolver(),
-                        Settings.Secure.USER_SETUP_COMPLETE, 1, userId);
-            } catch (RemoteException e) {
-                Log.i(LOG_TAG, "Can't talk to package manager", e);
-                return false;
-            } finally {
-                mInjector.binderRestoreCallingIdentity(id);
-            }
-            return true;
-        }
-    }
-
-    @Override
     public void setProfileEnabled(ComponentName who) {
         if (!mHasFeature) {
             return;
@@ -4674,7 +4629,8 @@
 
     // Returns the active profile owner for this user or null if the current user has no
     // profile owner.
-    private ActiveAdmin getProfileOwnerAdmin(int userHandle) {
+    @VisibleForTesting
+    ActiveAdmin getProfileOwnerAdminLocked(int userHandle) {
         ComponentName profileOwner = mOwners.getProfileOwnerComponent(userHandle);
         if (profileOwner == null) {
             return null;
@@ -5546,12 +5502,12 @@
     @Override
     public void setUserRestriction(ComponentName who, String key, boolean enabled) {
         Preconditions.checkNotNull(who, "ComponentName is null");
-        final int userHandle = UserHandle.getCallingUserId();
+        final int userHandle = mInjector.userHandleGetCallingUserId();
         final UserHandle user = new UserHandle(userHandle);
         synchronized (this) {
             ActiveAdmin activeAdmin =
                     getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
-            boolean isDeviceOwner = isDeviceOwner(activeAdmin.info.getPackageName());
+            boolean isDeviceOwner = isDeviceOwner(who);
             if (!isDeviceOwner && userHandle != UserHandle.USER_SYSTEM
                     && DEVICE_OWNER_USER_RESTRICTIONS.contains(key)) {
                 throw new SecurityException("Profile owners cannot set user restriction " + key);
@@ -5571,37 +5527,40 @@
                         mInjector.getIAudioService()
                                 .setMasterMute(true, 0, mContext.getPackageName(), userHandle);
                     } else if (UserManager.DISALLOW_CONFIG_WIFI.equals(key)) {
-                        Settings.Secure.putIntForUser(mContext.getContentResolver(),
+                        mInjector.settingsSecurePutIntForUser(
                                 Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, 0,
                                 userHandle);
                     } else if (UserManager.DISALLOW_SHARE_LOCATION.equals(key)) {
-                        Settings.Secure.putIntForUser(mContext.getContentResolver(),
+                        mInjector.settingsSecurePutIntForUser(
                                 Settings.Secure.LOCATION_MODE, Settings.Secure.LOCATION_MODE_OFF,
                                 userHandle);
-                        Settings.Secure.putStringForUser(mContext.getContentResolver(),
+                        mInjector.settingsSecurePutStringForUser(
                                 Settings.Secure.LOCATION_PROVIDERS_ALLOWED, "",
                                 userHandle);
                     } else if (UserManager.DISALLOW_DEBUGGING_FEATURES.equals(key)) {
                         // Only disable adb if changing for system user, since it is global
                         // TODO: should this be admin user?
                         if (userHandle == UserHandle.USER_SYSTEM) {
-                            Settings.Global.putStringForUser(mContext.getContentResolver(),
+                            mInjector.settingsGlobalPutStringForUser(
                                     Settings.Global.ADB_ENABLED, "0", userHandle);
                         }
                     } else if (UserManager.ENSURE_VERIFY_APPS.equals(key)) {
-                        Settings.Global.putStringForUser(mContext.getContentResolver(),
+                        mInjector.settingsGlobalPutStringForUser(
                                 Settings.Global.PACKAGE_VERIFIER_ENABLE, "1",
                                 userHandle);
-                        Settings.Global.putStringForUser(mContext.getContentResolver(),
+                        mInjector.settingsGlobalPutStringForUser(
                                 Settings.Global.PACKAGE_VERIFIER_INCLUDE_ADB, "1",
                                 userHandle);
                     } else if (UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES.equals(key)) {
-                        Settings.Secure.putIntForUser(mContext.getContentResolver(),
+                        mInjector.settingsSecurePutIntForUser(
                                 Settings.Secure.INSTALL_NON_MARKET_APPS, 0,
                                 userHandle);
                     }
                 }
                 mUserManager.setUserRestriction(key, enabled, user);
+                activeAdmin.ensureUserRestrictions().putBoolean(key, enabled);
+                saveSettingsLocked(userHandle);
+
                 if (enabled != alreadyRestricted) {
                     if (UserManager.DISALLOW_SHARE_LOCATION.equals(key)) {
                         // Send out notifications however as some clients may want to reread the
@@ -5902,7 +5861,7 @@
         // TODO: Should there be a check to make sure this relationship is within a profile group?
         //enforceSystemProcess("getCrossProfileCallerIdDisabled can only be called by system");
         synchronized (this) {
-            ActiveAdmin admin = getProfileOwnerAdmin(userId);
+            ActiveAdmin admin = getProfileOwnerAdminLocked(userId);
             return (admin != null) ? admin.disableCallerId : false;
         }
     }
@@ -5995,7 +5954,7 @@
         // within a profile group?
         // enforceSystemProcess("getCrossProfileCallerIdDisabled can only be called by system");
         synchronized (this) {
-            ActiveAdmin admin = getProfileOwnerAdmin(userId);
+            ActiveAdmin admin = getProfileOwnerAdminLocked(userId);
             return (admin != null) ? admin.disableBluetoothContactSharing : false;
         }
     }
@@ -6081,7 +6040,7 @@
             Bundle adminExtras = new Bundle();
             adminExtras.putString(DeviceAdminReceiver.EXTRA_LOCK_TASK_PACKAGE, pkg);
             for (ActiveAdmin admin : policy.mAdminList) {
-                boolean ownsDevice = isDeviceOwner(admin.info.getPackageName());
+                boolean ownsDevice = isDeviceOwner(admin.info.getComponent());
                 boolean ownsProfile = (getProfileOwner(userHandle) != null
                         && getProfileOwner(userHandle).equals(admin.info.getPackageName()));
                 if (ownsDevice || ownsProfile) {
@@ -6098,7 +6057,6 @@
 
     @Override
     public void setGlobalSetting(ComponentName who, String setting, String value) {
-        final ContentResolver contentResolver = mContext.getContentResolver();
         Preconditions.checkNotNull(who, "ComponentName is null");
 
         synchronized (this) {
@@ -6126,7 +6084,7 @@
 
             long id = mInjector.binderClearCallingIdentity();
             try {
-                Settings.Global.putString(contentResolver, setting, value);
+                mInjector.settingsGlobalPutString(setting, value);
             } finally {
                 mInjector.binderRestoreCallingIdentity(id);
             }
@@ -6140,10 +6098,9 @@
         final ContentResolver contentResolver = mContext.getContentResolver();
 
         synchronized (this) {
-            ActiveAdmin activeAdmin =
-                    getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+            getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
 
-            if (isDeviceOwner(activeAdmin.info.getPackageName())) {
+            if (isDeviceOwner(who)) {
                 if (!SECURE_SETTINGS_DEVICEOWNER_WHITELIST.contains(setting)) {
                     throw new SecurityException(String.format(
                             "Permission denial: Device owners cannot update %1$s", setting));
@@ -6155,7 +6112,7 @@
 
             long id = mInjector.binderClearCallingIdentity();
             try {
-                Settings.Secure.putStringForUser(contentResolver, setting, value, callingUserId);
+                mInjector.settingsSecurePutStringForUser(setting, value, callingUserId);
             } finally {
                 mInjector.binderRestoreCallingIdentity(id);
             }
@@ -6278,18 +6235,15 @@
      */
     void updateUserSetupComplete() {
         List<UserInfo> users = mUserManager.getUsers(true);
-        ContentResolver resolver = mContext.getContentResolver();
         final int N = users.size();
         for (int i = 0; i < N; i++) {
             int userHandle = users.get(i).id;
-            if (Settings.Secure.getIntForUser(resolver, Settings.Secure.USER_SETUP_COMPLETE, 0,
+            if (mInjector.settingsSecureGetIntForUser(Settings.Secure.USER_SETUP_COMPLETE, 0,
                     userHandle) != 0) {
                 DevicePolicyData policy = getUserData(userHandle);
                 if (!policy.mUserSetupComplete) {
                     policy.mUserSetupComplete = true;
                     synchronized (this) {
-                        // The DeviceInitializer was whitelisted but now should be removed.
-                        removeDeviceInitializerFromLockTaskPackages(userHandle);
                         saveSettingsLocked(userHandle);
                     }
                 }
@@ -6297,35 +6251,6 @@
         }
     }
 
-    private void addDeviceInitializerToLockTaskPackagesLocked(int userHandle) {
-        if (hasUserSetupCompleted(userHandle)) {
-            return;
-        }
-
-        final String deviceInitializerPackage = getDeviceInitializer();
-        if (deviceInitializerPackage == null) {
-            return;
-        }
-
-        final List<String> packages = getLockTaskPackagesLocked(userHandle);
-        if (!packages.contains(deviceInitializerPackage)) {
-            packages.add(deviceInitializerPackage);
-            setLockTaskPackagesLocked(userHandle, packages);
-        }
-    }
-
-    private void removeDeviceInitializerFromLockTaskPackages(int userHandle) {
-        final String deviceInitializerPackage = getDeviceInitializer();
-        if (deviceInitializerPackage == null) {
-            return;
-        }
-
-        List<String> packages = getLockTaskPackagesLocked(userHandle);
-        if (packages.remove(deviceInitializerPackage)) {
-            setLockTaskPackagesLocked(userHandle, packages);
-        }
-    }
-
     private class SetupContentObserver extends ContentObserver {
 
         private final Uri mUserSetupComplete = Settings.Secure.getUriFor(
@@ -6450,15 +6375,15 @@
     }
 
     /**
-     * Checks if the caller of the method is the device owner app or device initialization app.
+     * Checks if the caller of the method is the device owner app.
      *
      * @param callerUid UID of the caller.
-     * @return true if the caller is the device owner app or device initializer.
+     * @return true if the caller is the device owner app
      */
-    private boolean isCallerDeviceOwnerOrInitializer(int callerUid) {
+    private boolean isCallerDeviceOwner(int callerUid) {
         String[] pkgs = mContext.getPackageManager().getPackagesForUid(callerUid);
         for (String pkg : pkgs) {
-            if (isDeviceOwner(pkg) || isDeviceInitializer(pkg)) {
+            if (isDeviceOwnerPackage(pkg)) {
                 return true;
             }
         }
@@ -6480,7 +6405,8 @@
                 updateReceivedTime);
 
         synchronized (this) {
-            String deviceOwnerPackage = getDeviceOwner();
+            final String deviceOwnerPackage = getDeviceOwner() == null ? null :
+                    getDeviceOwner().getPackageName();
             if (deviceOwnerPackage == null) {
                 return;
             }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
index 370cf48..799267d 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
@@ -16,16 +16,11 @@
 
 package com.android.server.devicepolicy;
 
-import android.app.AppGlobals;
 import android.app.admin.SystemUpdatePolicy;
 import android.content.ComponentName;
 import android.content.Context;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.UserInfo;
 import android.os.Environment;
-import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.util.ArrayMap;
@@ -35,6 +30,7 @@
 import android.util.Xml;
 
 import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.Preconditions;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -90,9 +86,6 @@
 
     private int mDeviceOwnerUserId = UserHandle.USER_NULL;
 
-    // Internal state for the device initializer package.
-    private OwnerInfo mDeviceInitializer;
-
     // Internal state for the profile owner packages.
     private final ArrayMap<Integer, OwnerInfo> mProfileOwners = new ArrayMap<>();
 
@@ -153,12 +146,16 @@
         return mDeviceOwner != null ? mDeviceOwner.name : null;
     }
 
-    void setDeviceOwner(String packageName, String ownerName, int userId) {
+    ComponentName getDeviceOwnerComponent() {
+        return mDeviceOwner != null ? mDeviceOwner.admin : null;
+    }
+
+    void setDeviceOwner(ComponentName admin, String ownerName, int userId) {
         if (userId < 0) {
             Slog.e(TAG, "Invalid user id for device owner user: " + userId);
             return;
         }
-        mDeviceOwner = new OwnerInfo(ownerName, packageName);
+        mDeviceOwner = new OwnerInfo(ownerName, admin);
         mDeviceOwnerUserId = userId;
     }
 
@@ -167,26 +164,6 @@
         mDeviceOwnerUserId = UserHandle.USER_NULL;
     }
 
-    ComponentName getDeviceInitializerComponent() {
-        return mDeviceInitializer.admin;
-    }
-
-    String getDeviceInitializerPackageName() {
-        return mDeviceInitializer != null ? mDeviceInitializer.packageName : null;
-    }
-
-    void setDeviceInitializer(ComponentName admin) {
-        mDeviceInitializer = new OwnerInfo(null, admin);
-    }
-
-    void clearDeviceInitializer() {
-        mDeviceInitializer = null;
-    }
-
-    boolean hasDeviceInitializer() {
-        return mDeviceInitializer != null;
-    }
-
     void setProfileOwner(ComponentName admin, String ownerName, int userId) {
         mProfileOwners.put(userId, new OwnerInfo(ownerName, admin));
     }
@@ -252,18 +229,7 @@
                     mDeviceOwner = new OwnerInfo(name, packageName);
                     mDeviceOwnerUserId = UserHandle.USER_SYSTEM;
                 } else if (tag.equals(TAG_DEVICE_INITIALIZER)) {
-                    String packageName = parser.getAttributeValue(null, ATTR_PACKAGE);
-                    String initializerComponentStr =
-                            parser.getAttributeValue(null, ATTR_COMPONENT_NAME);
-                    ComponentName admin =
-                            ComponentName.unflattenFromString(initializerComponentStr);
-                    if (admin != null) {
-                        mDeviceInitializer = new OwnerInfo(null, admin);
-                    } else {
-                        mDeviceInitializer = new OwnerInfo(null, packageName);
-                        Slog.e(TAG, "Error parsing device-owner file. Bad component name " +
-                                initializerComponentStr);
-                    }
+                    // Deprecated tag
                 } else if (tag.equals(TAG_PROFILE_OWNER)) {
                     String profileOwnerPackageName = parser.getAttributeValue(null, ATTR_PACKAGE);
                     String profileOwnerName = parser.getAttributeValue(null, ATTR_NAME);
@@ -444,8 +410,7 @@
 
         @Override
         boolean shouldWrite() {
-            return (mDeviceOwner != null) || (mDeviceInitializer != null)
-                    || (mSystemUpdatePolicy != null);
+            return (mDeviceOwner != null) || (mSystemUpdatePolicy != null);
         }
 
         @Override
@@ -457,9 +422,6 @@
                 out.endTag(null, TAG_DEVICE_OWNER_CONTEXT);
             }
 
-            if (mDeviceInitializer != null) {
-                mDeviceInitializer.writeToXml(out, TAG_DEVICE_INITIALIZER);
-            }
             if (mSystemUpdatePolicy != null) {
                 out.startTag(null, TAG_SYSTEM_UPDATE_POLICY);
                 mSystemUpdatePolicy.saveToXml(out);
@@ -488,7 +450,7 @@
                     break;
                 }
                 case TAG_DEVICE_INITIALIZER:
-                    mDeviceInitializer = OwnerInfo.readFromXml(parser);
+                    // Deprecated tag
                     break;
                 case TAG_SYSTEM_UPDATE_POLICY:
                     mSystemUpdatePolicy = SystemUpdatePolicy.restoreFromXml(parser);
@@ -584,7 +546,7 @@
                 } else {
                     // This shouldn't happen but switch from package name -> component name
                     // might have written bad device owner files. b/17652534
-                    Slog.e(TAG, "Error parsing device-owner file. Bad component name " +
+                    Slog.e(TAG, "Error parsing owner file. Bad component name " +
                             componentName);
                 }
             }
@@ -607,11 +569,6 @@
             pw.println(prefix + "  User ID: " + mDeviceOwnerUserId);
             pw.println();
         }
-        if (mDeviceInitializer != null) {
-            pw.println(prefix + "Device Initializer: ");
-            mDeviceInitializer.dump(prefix + "  ", pw);
-            pw.println();
-        }
         if (mSystemUpdatePolicy != null) {
             pw.println(prefix + "System Update Policy: " + mSystemUpdatePolicy);
             pw.println();
diff --git a/services/net/java/android/net/ip/IpReachabilityMonitor.java b/services/net/java/android/net/ip/IpReachabilityMonitor.java
index 982bae0..53f55cd 100644
--- a/services/net/java/android/net/ip/IpReachabilityMonitor.java
+++ b/services/net/java/android/net/ip/IpReachabilityMonitor.java
@@ -423,7 +423,7 @@
                 try {
                     byteBuffer = recvKernelReply();
                 } catch (ErrnoException e) {
-                    Log.w(TAG, "ErrnoException: ", e);
+                    if (stillRunning()) { Log.w(TAG, "ErrnoException: ", e); }
                     break;
                 }
                 final long whenMs = SystemClock.elapsedRealtime();
diff --git a/services/tests/servicestests/src/com/android/server/MountServiceTests.java b/services/tests/servicestests/src/com/android/server/MountServiceTests.java
index 9c88b40..ecfe0db 100644
--- a/services/tests/servicestests/src/com/android/server/MountServiceTests.java
+++ b/services/tests/servicestests/src/com/android/server/MountServiceTests.java
@@ -27,8 +27,6 @@
 import android.test.suitebuilder.annotation.LargeTest;
 import android.util.Log;
 
-import static com.android.server.MountService.buildObbPath;
-
 import com.android.frameworks.servicestests.R;
 
 import java.io.File;
@@ -42,16 +40,6 @@
 
     private static final String OBB_MOUNT_PREFIX = "/mnt/obb/";
 
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-    }
-
-    @Override
-    protected void tearDown() throws Exception {
-        super.tearDown();
-    }
-
     private static void assertStartsWith(String message, String prefix, String actual) {
         if (!actual.startsWith(prefix)) {
             throw new ComparisonFailure(message, prefix, actual);
@@ -195,8 +183,7 @@
         final ObbObserver observer = new ObbObserver();
 
         assertTrue("unmountObb call on test1.obb should succeed",
- sm.unmountObb(file.getPath(),
-                false, observer));
+                sm.unmountObb(file.getPath(), false, observer));
 
         assertTrue("Unmount should have completed",
                 observer.waitForCompletion());
@@ -284,34 +271,4 @@
         unmountObb(sm, file1, OnObbStateChangeListener.UNMOUNTED);
         unmountObb(sm, file2, OnObbStateChangeListener.UNMOUNTED);
     }
-
-    public void testBuildObbPath() {
-        final int userId = 10;
-
-        // Paths outside external storage should remain untouched
-        assertEquals("/storage/random/foo",
-                buildObbPath("/storage/random/foo", userId, true));
-        assertEquals("/storage/random/foo",
-                buildObbPath("/storage/random/foo", userId, false));
-
-        // Paths on user-specific emulated storage
-        assertEquals("/mnt/shell/emulated/10/foo",
-                buildObbPath("/storage/emulated_legacy/foo", userId, true));
-        assertEquals("/storage/emulated/10/foo",
-                buildObbPath("/storage/emulated_legacy/foo", userId, false));
-        assertEquals("/mnt/shell/emulated/10/foo",
-                buildObbPath("/storage/emulated/10/foo", userId, true));
-        assertEquals("/storage/emulated/10/foo",
-                buildObbPath("/storage/emulated/10/foo", userId, false));
-
-        // Paths on shared OBB emulated storage
-        assertEquals("/mnt/shell/emulated/obb/foo",
-                buildObbPath("/storage/emulated_legacy/Android/obb/foo", userId, true));
-        assertEquals("/storage/emulated/0/Android/obb/foo",
-                buildObbPath("/storage/emulated_legacy/Android/obb/foo", userId, false));
-        assertEquals("/mnt/shell/emulated/obb/foo",
-                buildObbPath("/storage/emulated/10/Android/obb/foo", userId, true));
-        assertEquals("/storage/emulated/0/Android/obb/foo",
-                buildObbPath("/storage/emulated/10/Android/obb/foo", userId, false));
-    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
index c6c7497..b109e7b 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
@@ -50,11 +50,11 @@
         private final File mDeviceOwnerFile;
         private final File mProfileOwnerBase;
 
-        public OwnersTestable(Context context, File dataDir) {
+        public OwnersTestable(DpmMockContext context) {
             super(context);
-            mLegacyFile = new File(dataDir, LEGACY_FILE);
-            mDeviceOwnerFile = new File(dataDir, DEVICE_OWNER_FILE);
-            mProfileOwnerBase = new File(dataDir, PROFILE_OWNER_FILE_BASE);
+            mLegacyFile = new File(context.dataDir, LEGACY_FILE);
+            mDeviceOwnerFile = new File(context.dataDir, DEVICE_OWNER_FILE);
+            mProfileOwnerBase = new File(context.dataDir, PROFILE_OWNER_FILE_BASE);
         }
 
         @Override
@@ -90,27 +90,15 @@
 
         public final File dataDir;
 
-        public final File systemUserDataDir;
-        public final File secondUserDataDir;
-
         private MockInjector(DpmMockContext context, File dataDir) {
             super(context);
             this.context = context;
             this.dataDir = dataDir;
-
-            systemUserDataDir = new File(dataDir, "user0");
-            DpmTestUtils.clearDir(dataDir);
-
-            secondUserDataDir = new File(dataDir, "user" + DpmMockContext.CALLER_USER_HANDLE);
-            DpmTestUtils.clearDir(secondUserDataDir);
-
-            when(context.environment.getUserSystemDirectory(
-                    eq(DpmMockContext.CALLER_USER_HANDLE))).thenReturn(secondUserDataDir);
         }
 
         @Override
         Owners newOwners() {
-            return new OwnersTestable(context, dataDir);
+            return new OwnersTestable(context);
         }
 
         @Override
@@ -165,7 +153,7 @@
 
         @Override
         String getDevicePolicyFilePathForSystemUser() {
-            return systemUserDataDir.getAbsolutePath();
+            return context.systemUserDataDir.getAbsolutePath();
         }
 
         @Override
@@ -237,5 +225,45 @@
         boolean userManagerIsSplitSystemUser() {
             return context.userManagerForMock.isSplitSystemUser();
         }
+
+        @Override
+        int settingsSecureGetIntForUser(String name, int def, int userHandle) {
+            return context.settings.settingsSecureGetIntForUser(name, def, userHandle);
+        }
+
+        @Override
+        void settingsSecurePutIntForUser(String name, int value, int userHandle) {
+            context.settings.settingsSecurePutIntForUser(name, value, userHandle);
+        }
+
+        @Override
+        void settingsSecurePutStringForUser(String name, String value, int userHandle) {
+            context.settings.settingsSecurePutStringForUser(name, value, userHandle);
+        }
+
+        @Override
+        void settingsGlobalPutStringForUser(String name, String value, int userHandle) {
+            context.settings.settingsGlobalPutStringForUser(name, value, userHandle);
+        }
+
+        @Override
+        void settingsSecurePutInt(String name, int value) {
+            context.settings.settingsSecurePutInt(name, value);
+        }
+
+        @Override
+        void settingsGlobalPutInt(String name, int value) {
+            context.settings.settingsGlobalPutInt(name, value);
+        }
+
+        @Override
+        void settingsSecurePutString(String name, String value) {
+            context.settings.settingsSecurePutString(name, value);
+        }
+
+        @Override
+        void settingsGlobalPutString(String name, String value) {
+            context.settings.settingsGlobalPutString(name, value);
+        }
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 0072f52..d6a60c7 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -16,6 +16,7 @@
 package com.android.server.devicepolicy;
 
 import com.android.server.LocalServices;
+import com.android.server.SystemService;
 
 import android.Manifest.permission;
 import android.app.Activity;
@@ -31,8 +32,8 @@
 import android.content.pm.ResolveInfo;
 import android.os.Bundle;
 import android.content.pm.PackageInfo;
-import android.content.pm.UserInfo;
 import android.os.UserHandle;
+import android.os.UserManager;
 import android.util.Pair;
 
 import org.mockito.ArgumentCaptor;
@@ -44,6 +45,7 @@
 import java.util.Map;
 
 import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Matchers.isNull;
@@ -65,8 +67,6 @@
  (mmma frameworks/base/services/tests/servicestests/ for non-ninja build)
  */
 public class DevicePolicyManagerTest extends DpmTestBase {
-
-
     private DpmMockContext mContext;
     public DevicePolicyManager dpm;
     public DevicePolicyManagerServiceTestable dpms;
@@ -83,29 +83,46 @@
         when(mContext.packageManager.hasSystemFeature(eq(PackageManager.FEATURE_DEVICE_ADMIN)))
                 .thenReturn(true);
 
-        LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
-        dpms = new DevicePolicyManagerServiceTestable(mContext, dataDir);
-        dpm = new DevicePolicyManagerTestable(mContext, dpms);
+        initializeDpms();
 
         admin1 = new ComponentName(mRealTestContext, DummyDeviceAdmins.Admin1.class);
         admin2 = new ComponentName(mRealTestContext, DummyDeviceAdmins.Admin2.class);
         admin3 = new ComponentName(mRealTestContext, DummyDeviceAdmins.Admin3.class);
 
-        setUpPackageManagerForAdmin(admin1);
-        setUpPackageManagerForAdmin(admin2);
-        setUpPackageManagerForAdmin(admin3);
+        setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_UID);
+        setUpPackageManagerForAdmin(admin2, DpmMockContext.CALLER_UID);
+        setUpPackageManagerForAdmin(admin3, DpmMockContext.CALLER_UID);
 
-        setUpApplicationInfo(PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED);
-        setUpPackageInfo();
         setUpUserManager();
     }
 
-    /**
-     * Set up a mock result for {@link PackageManager#queryBroadcastReceivers}.  We'll return
-     * the actual ResolveInfo for the admin component, but we need to mock PM so it'll return
-     * it for user {@link DpmMockContext#CALLER_USER_HANDLE}.
-     */
-    private void setUpPackageManagerForAdmin(ComponentName admin) {
+    private void initializeDpms() {
+        // Need clearCallingIdentity() to pass permission checks.
+        final long ident = mContext.binder.clearCallingIdentity();
+        try {
+            LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
+
+            dpms = new DevicePolicyManagerServiceTestable(mContext, dataDir);
+
+            dpms.systemReady(SystemService.PHASE_LOCK_SETTINGS_READY);
+            dpms.systemReady(SystemService.PHASE_BOOT_COMPLETED);
+
+            dpm = new DevicePolicyManagerTestable(mContext, dpms);
+        } finally {
+            mContext.binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    private void setUpPackageManagerForAdmin(ComponentName admin, int packageUid) throws Exception {
+        setUpPackageManagerForAdmin(admin, packageUid,
+                PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED);
+    }
+
+    private void setUpPackageManagerForAdmin(ComponentName admin, int packageUid,
+            int enabledSetting) throws Exception {
+
+        // Set up queryBroadcastReceivers().
+
         final Intent resolveIntent = new Intent();
         resolveIntent.setComponent(admin);
         final List<ResolveInfo> realResolveInfo =
@@ -115,51 +132,46 @@
         assertNotNull(realResolveInfo);
         assertEquals(1, realResolveInfo.size());
 
+        // We need to change AI, so set a clone.
+        realResolveInfo.set(0, DpmTestUtils.cloneParcelable(realResolveInfo.get(0)));
+
         // We need to rewrite the UID in the activity info.
-        realResolveInfo.get(0).activityInfo.applicationInfo.uid = DpmMockContext.CALLER_UID;
+        realResolveInfo.get(0).activityInfo.applicationInfo.uid = packageUid;
 
         doReturn(realResolveInfo).when(mContext.packageManager).queryBroadcastReceivers(
                 MockUtils.checkIntentComponent(admin),
                 eq(PackageManager.GET_META_DATA
                         | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS),
-                eq(DpmMockContext.CALLER_USER_HANDLE)
-        );
-    }
+                eq(UserHandle.getUserId(packageUid)));
 
-    /**
-     * Set up a mock result for {@link IPackageManager#getApplicationInfo} for user
-     * {@link DpmMockContext#CALLER_USER_HANDLE}.
-     */
-    private void setUpApplicationInfo(int enabledSetting) throws Exception {
-        final ApplicationInfo ai = mRealTestContext.getPackageManager().getApplicationInfo(
-                admin1.getPackageName(),
-                PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS);
+        // Set up getApplicationInfo().
+
+        final ApplicationInfo ai = DpmTestUtils.cloneParcelable(
+                mRealTestContext.getPackageManager().getApplicationInfo(
+                        admin1.getPackageName(),
+                        PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS));
 
         ai.enabledSetting = enabledSetting;
+        ai.uid = packageUid;
 
         doReturn(ai).when(mContext.ipackageManager).getApplicationInfo(
                 eq(admin1.getPackageName()),
                 eq(PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS),
-                eq(DpmMockContext.CALLER_USER_HANDLE));
-    }
+                eq(UserHandle.getUserId(packageUid)));
 
-    /**
-     * Set up a mock result for {@link IPackageManager#getPackageInfo(String, int, int)} for user
-     * {@link DpmMockContext#CALLER_USER_HANDLE} as well as the system user.
-     */
-    private void setUpPackageInfo() throws Exception {
-        final PackageInfo pi = mRealTestContext.getPackageManager().getPackageInfo(
-                admin1.getPackageName(), 0);
+        // Set up getPackageInfo().
+
+        final PackageInfo pi = DpmTestUtils.cloneParcelable(
+                mRealTestContext.getPackageManager().getPackageInfo(
+                        admin1.getPackageName(), 0));
         assertTrue(pi.applicationInfo.flags != 0);
 
+        pi.applicationInfo.uid = packageUid;
+
         doReturn(pi).when(mContext.ipackageManager).getPackageInfo(
                 eq(admin1.getPackageName()),
                 eq(0),
-                eq(DpmMockContext.CALLER_USER_HANDLE));
-        doReturn(pi).when(mContext.ipackageManager).getPackageInfo(
-                eq(admin1.getPackageName()),
-                eq(0),
-                eq(UserHandle.USER_SYSTEM));
+                eq(UserHandle.getUserId(packageUid)));
     }
 
     private void setUpUserManager() {
@@ -193,25 +205,15 @@
         }).when(mContext.userManager).getApplicationRestrictions(
                 anyString(), any(UserHandle.class));
 
-        // System user is always running.
-        when(mContext.userManager.isUserRunning(MockUtils.checkUserHandle(UserHandle.USER_SYSTEM)))
-                .thenReturn(true);
-
-        // Set up (default) UserInfo for CALLER_USER_HANDLE.
-        final UserInfo uh = new UserInfo(DpmMockContext.CALLER_USER_HANDLE,
-                "user" + DpmMockContext.CALLER_USER_HANDLE, 0);
-
-        when(mContext.userManager.getUserInfo(eq(DpmMockContext.CALLER_USER_HANDLE)))
-                .thenReturn(uh);
+        // Add the first secondary user.
+        mContext.addUser(DpmMockContext.CALLER_USER_HANDLE, 0);
     }
 
     private void setAsProfileOwner(ComponentName admin) {
         mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS);
         mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
 
-        final UserInfo uh = new UserInfo(DpmMockContext.CALLER_USER_HANDLE, "user", 0);
-
-        // DO needs to be an DA.
+        // PO needs to be an DA.
         dpm.setActiveAdmin(admin, /* replace =*/ false);
 
         // Fire!
@@ -309,7 +311,8 @@
 
         // Next, add one more admin.
         // Before doing so, update the application info, now it's enabled.
-        setUpApplicationInfo(PackageManager.COMPONENT_ENABLED_STATE_ENABLED);
+        setUpPackageManagerForAdmin(admin2, DpmMockContext.CALLER_UID,
+                PackageManager.COMPONENT_ENABLED_STATE_ENABLED);
 
         dpm.setActiveAdmin(admin2, /* replace =*/ false);
 
@@ -354,6 +357,33 @@
         mContext.callerPermissions.remove("android.permission.INTERACT_ACROSS_USERS_FULL");
     }
 
+    public void testSetActiveAdmin_multiUsers() throws Exception {
+
+        final int ANOTHER_USER_ID = 100;
+        final int ANOTHER_ADMIN_UID = UserHandle.getUid(ANOTHER_USER_ID, 20456);
+
+        mMockContext.addUser(ANOTHER_USER_ID, 0); // Add one more user.
+
+        // Set up pacakge manager for the other user.
+        setUpPackageManagerForAdmin(admin2, ANOTHER_ADMIN_UID);
+
+        mContext.callerPermissions.add(android.Manifest.permission.MANAGE_DEVICE_ADMINS);
+
+        dpm.setActiveAdmin(admin1, /* replace =*/ false);
+
+        mMockContext.binder.callingUid = ANOTHER_ADMIN_UID;
+        dpm.setActiveAdmin(admin2, /* replace =*/ false);
+
+
+        mMockContext.binder.callingUid = DpmMockContext.CALLER_UID;
+        assertTrue(dpm.isAdminActive(admin1));
+        assertFalse(dpm.isAdminActive(admin2));
+
+        mMockContext.binder.callingUid = ANOTHER_ADMIN_UID;
+        assertFalse(dpm.isAdminActive(admin1));
+        assertTrue(dpm.isAdminActive(admin2));
+    }
+
     /**
      * Test for:
      * {@link DevicePolicyManager#setActiveAdmin}
@@ -400,9 +430,11 @@
         // having MANAGE_DEVICE_ADMINS.
         mContext.callerPermissions.clear();
 
+        // Change the caller, and call into DPMS directly with a different user-id.
+
         mContext.binder.callingUid = 1234567;
         try {
-            dpm.removeActiveAdmin(admin1);
+            dpms.removeActiveAdmin(admin1, DpmMockContext.CALLER_USER_HANDLE);
             fail("Didn't throw SecurityException");
         } catch (SecurityException expected) {
         }
@@ -412,7 +444,7 @@
      * Test for:
      * {@link DevicePolicyManager#removeActiveAdmin}
      */
-    public void testRemoveActiveAdmin_fromDifferentUserWithMINTERACT_ACROSS_USERS_FULL() {
+    public void testRemoveActiveAdmin_fromDifferentUserWithINTERACT_ACROSS_USERS_FULL() {
         mContext.callerPermissions.add(android.Manifest.permission.MANAGE_DEVICE_ADMINS);
 
         // Add admin1.
@@ -424,8 +456,11 @@
 
         // Different user, but should work, because caller has proper permissions.
         mContext.callerPermissions.add(permission.INTERACT_ACROSS_USERS_FULL);
+
+        // Change the caller, and call into DPMS directly with a different user-id.
         mContext.binder.callingUid = 1234567;
-        dpm.removeActiveAdmin(admin1);
+
+        dpms.removeActiveAdmin(admin1, DpmMockContext.CALLER_USER_HANDLE);
 
         assertTrue(dpm.isRemovingAdmin(admin1, DpmMockContext.CALLER_USER_HANDLE));
 
@@ -498,14 +533,17 @@
         mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
         mContext.callerPermissions.add(permission.INTERACT_ACROSS_USERS_FULL);
 
-        // Call from a process on the system user.
+        // In this test, change the caller user to "system".
         mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
 
+        // Make sure admin1 is installed on system user.
+        setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_SYSTEM_USER_UID);
+
         // DO needs to be an DA.
         dpm.setActiveAdmin(admin1, /* replace =*/ false);
 
         // Fire!
-        assertTrue(dpm.setDeviceOwner(admin1.getPackageName(), "owner-name"));
+        assertTrue(dpm.setDeviceOwner(admin1, "owner-name"));
 
         // Verify internal calls.
         verify(mContext.iactivityManager, times(1)).updateDeviceOwner(
@@ -536,10 +574,8 @@
         // Call from a process on the system user.
         mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
 
-        // DO needs to be an DA.
-        dpm.setActiveAdmin(admin1, /* replace =*/ false);
         try {
-            dpm.setDeviceOwner("a.b.c");
+            dpm.setDeviceOwner(new ComponentName("a.b.c", ".def"));
             fail("Didn't throw IllegalArgumentException");
         } catch (IllegalArgumentException expected) {
         }
@@ -549,6 +585,85 @@
         // TODO Test more failure cases.  Basically test all chacks in enforceCanSetDeviceOwner().
     }
 
+    public void testClearDeviceOwner() throws Exception {
+        mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS);
+        mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
+        mContext.callerPermissions.add(permission.INTERACT_ACROSS_USERS_FULL);
+
+        // Set admin1 as a DA to the secondary user.
+        setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_UID);
+
+        dpm.setActiveAdmin(admin1, /* replace =*/ false);
+
+        // Set admin 1 as the DO to the system user.
+
+        mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+        setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_SYSTEM_USER_UID);
+        dpm.setActiveAdmin(admin1, /* replace =*/ false);
+        assertTrue(dpm.setDeviceOwner(admin1, "owner-name"));
+
+        // Verify internal calls.
+        verify(mContext.iactivityManager, times(1)).updateDeviceOwner(
+                eq(admin1.getPackageName()));
+
+        assertEquals(admin1.getPackageName(), dpm.getDeviceOwner());
+
+        // Set up other mocks.
+        when(mContext.userManager.getUserRestrictions()).thenReturn(new Bundle());
+
+        // Now call clear.
+        doReturn(DpmMockContext.CALLER_SYSTEM_USER_UID).when(mContext.packageManager).getPackageUid(
+                eq(admin1.getPackageName()),
+                anyInt());
+        dpm.clearDeviceOwnerApp(admin1.getPackageName());
+
+        // Now DO shouldn't be set.
+        assertNull(dpm.getDeviceOwner());
+
+        // TODO Check other calls.
+    }
+
+    public void testClearDeviceOwner_fromDifferentUser() throws Exception {
+        mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS);
+        mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
+        mContext.callerPermissions.add(permission.INTERACT_ACROSS_USERS_FULL);
+
+        // Set admin1 as a DA to the secondary user.
+        setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_UID);
+
+        dpm.setActiveAdmin(admin1, /* replace =*/ false);
+
+        // Set admin 1 as the DO to the system user.
+
+        mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+        setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_SYSTEM_USER_UID);
+        dpm.setActiveAdmin(admin1, /* replace =*/ false);
+        assertTrue(dpm.setDeviceOwner(admin1, "owner-name"));
+
+        // Verify internal calls.
+        verify(mContext.iactivityManager, times(1)).updateDeviceOwner(
+                eq(admin1.getPackageName()));
+
+        assertEquals(admin1.getPackageName(), dpm.getDeviceOwner());
+
+        // Now call clear from the secondary user, which should throw.
+        mContext.binder.callingUid = DpmMockContext.CALLER_UID;
+
+        // Now call clear.
+        doReturn(DpmMockContext.CALLER_UID).when(mContext.packageManager).getPackageUid(
+                eq(admin1.getPackageName()),
+                anyInt());
+        try {
+            dpm.clearDeviceOwnerApp(admin1.getPackageName());
+            fail("Didn't throw");
+        } catch (SecurityException e) {
+            assertEquals("clearDeviceOwner can only be called by the device owner", e.getMessage());
+        }
+
+        // Now DO shouldn't be set.
+        assertNotNull(dpm.getDeviceOwner());
+    }
+
     public void testSetProfileOwner() throws Exception {
         setAsProfileOwner(admin1);
     }
@@ -557,6 +672,85 @@
         // TODO Test more failure cases.  Basically test all chacks in enforceCanSetProfileOwner().
     }
 
+    public void testGetDeviceOwnerAdminLocked() throws Exception {
+        checkDeviceOwnerWithMultipleDeviceAdmins();
+    }
+
+    private void checkDeviceOwnerWithMultipleDeviceAdmins() throws Exception {
+        // In ths test, we use 3 users (system + 2 secondary users), set some device admins to them,
+        // set admin2 on CALLER_USER_HANDLE as DO, then call getDeviceOwnerAdminLocked() to
+        // make sure it gets the right component from the right user.
+
+        final int ANOTHER_USER_ID = 100;
+        final int ANOTHER_ADMIN_UID = UserHandle.getUid(ANOTHER_USER_ID, 456);
+
+        mMockContext.addUser(ANOTHER_USER_ID, 0); // Add one more user.
+
+        mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS);
+        mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
+        mContext.callerPermissions.add(permission.INTERACT_ACROSS_USERS_FULL);
+
+        mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+
+        // Make sure the admin packge is installed to each user.
+        setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_SYSTEM_USER_UID);
+        setUpPackageManagerForAdmin(admin3, DpmMockContext.CALLER_SYSTEM_USER_UID);
+
+        setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_UID);
+        setUpPackageManagerForAdmin(admin2, DpmMockContext.CALLER_UID);
+
+        setUpPackageManagerForAdmin(admin2, ANOTHER_ADMIN_UID);
+
+
+        // Set active admins to the users.
+        dpm.setActiveAdmin(admin1, /* replace =*/ false);
+        dpm.setActiveAdmin(admin3, /* replace =*/ false);
+
+        dpm.setActiveAdmin(admin1, /* replace =*/ false, DpmMockContext.CALLER_USER_HANDLE);
+        dpm.setActiveAdmin(admin2, /* replace =*/ false, DpmMockContext.CALLER_USER_HANDLE);
+
+        dpm.setActiveAdmin(admin2, /* replace =*/ false, ANOTHER_USER_ID);
+
+        // Set DO on the first non-system user.
+        mContext.setUserRunning(DpmMockContext.CALLER_USER_HANDLE, true);
+        assertTrue(dpm.setDeviceOwner(admin2, "owner-name", DpmMockContext.CALLER_USER_HANDLE));
+
+        // Make sure it's set.
+        assertEquals(admin2, dpm.getDeviceOwnerComponent());
+
+        // Then check getDeviceOwnerAdminLocked().
+        assertEquals(admin2, dpms.getDeviceOwnerAdminLocked().info.getComponent());
+        assertEquals(DpmMockContext.CALLER_UID, dpms.getDeviceOwnerAdminLocked().getUid());
+    }
+
+    /**
+     * This essentially tests
+     * {@code DevicePolicyManagerService.findOwnerComponentIfNecessaryLocked()}. (which is private.)
+     *
+     * We didn't use to persist the DO component class name, but now we do, and the above method
+     * finds the right component from a package name upon migration.
+     */
+    public void testDeviceOwnerMigration() throws Exception {
+        checkDeviceOwnerWithMultipleDeviceAdmins();
+
+        // Overwrite the device owner setting and clears the clas name.
+        dpms.mOwners.setDeviceOwner(
+                new ComponentName(admin2.getPackageName(), ""),
+                "owner-name", DpmMockContext.CALLER_USER_HANDLE);
+        dpms.mOwners.writeDeviceOwner();
+
+        // Make sure the DO component name doesn't have a class name.
+        assertEquals("", dpms.getDeviceOwner().getClassName());
+
+        // Then create a new DPMS to have it load the settings from files.
+        initializeDpms();
+
+        // Now the DO component name is a full name.
+        // *BUT* because both admin1 and admin2 belong to the same package, we think admin1 is the
+        // DO.
+        assertEquals(admin1, dpms.getDeviceOwner());
+    }
+
     public void testSetGetApplicationRestriction() {
         setAsProfileOwner(admin1);
 
@@ -589,4 +783,95 @@
         dpm.setApplicationRestrictions(admin1, "pkg2", new Bundle());
         assertEquals(0, dpm.getApplicationRestrictions(admin1, "pkg2").size());
     }
+
+    public void testSetUserRestriction_asDo() throws Exception {
+        mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS);
+        mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
+        mContext.callerPermissions.add(permission.INTERACT_ACROSS_USERS_FULL);
+
+        // First, set DO.
+
+        // Call from a process on the system user.
+        mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+
+        // Make sure admin1 is installed on system user.
+        setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_SYSTEM_USER_UID);
+
+        // Call.
+        dpm.setActiveAdmin(admin1, /* replace =*/ false, UserHandle.USER_SYSTEM);
+        assertTrue(dpm.setDeviceOwner(admin1, "owner-name",
+                UserHandle.USER_SYSTEM));
+
+        assertFalse(dpms.getDeviceOwnerAdminLocked().ensureUserRestrictions()
+                .getBoolean(UserManager.DISALLOW_SMS));
+        assertFalse(dpms.getDeviceOwnerAdminLocked().ensureUserRestrictions()
+                .getBoolean(UserManager.DISALLOW_OUTGOING_CALLS));
+
+        dpm.addUserRestriction(admin1, UserManager.DISALLOW_SMS);
+        dpm.addUserRestriction(admin1, UserManager.DISALLOW_OUTGOING_CALLS);
+
+        assertTrue(dpms.getDeviceOwnerAdminLocked().ensureUserRestrictions()
+                .getBoolean(UserManager.DISALLOW_SMS));
+        assertTrue(dpms.getDeviceOwnerAdminLocked().ensureUserRestrictions()
+                .getBoolean(UserManager.DISALLOW_OUTGOING_CALLS));
+
+        dpm.clearUserRestriction(admin1, UserManager.DISALLOW_SMS);
+
+        assertFalse(dpms.getDeviceOwnerAdminLocked().ensureUserRestrictions()
+                .getBoolean(UserManager.DISALLOW_SMS));
+        assertTrue(dpms.getDeviceOwnerAdminLocked().ensureUserRestrictions()
+                .getBoolean(UserManager.DISALLOW_OUTGOING_CALLS));
+
+        dpm.clearUserRestriction(admin1, UserManager.DISALLOW_OUTGOING_CALLS);
+
+        assertFalse(dpms.getDeviceOwnerAdminLocked().ensureUserRestrictions()
+                .getBoolean(UserManager.DISALLOW_SMS));
+        assertFalse(dpms.getDeviceOwnerAdminLocked().ensureUserRestrictions()
+                .getBoolean(UserManager.DISALLOW_OUTGOING_CALLS));
+
+        // TODO Check inner calls.
+        // TODO Make sure restrictions are written to the file.
+    }
+
+    public void testSetUserRestriction_asPo() {
+        setAsProfileOwner(admin1);
+
+        assertFalse(dpms.getProfileOwnerAdminLocked(DpmMockContext.CALLER_USER_HANDLE)
+                .ensureUserRestrictions()
+                .getBoolean(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES));
+        assertFalse(dpms.getProfileOwnerAdminLocked(DpmMockContext.CALLER_USER_HANDLE)
+                .ensureUserRestrictions()
+                .getBoolean(UserManager.DISALLOW_OUTGOING_CALLS));
+
+        dpm.addUserRestriction(admin1, UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES);
+        dpm.addUserRestriction(admin1, UserManager.DISALLOW_OUTGOING_CALLS);
+
+        assertTrue(dpms.getProfileOwnerAdminLocked(DpmMockContext.CALLER_USER_HANDLE)
+                .ensureUserRestrictions()
+                .getBoolean(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES));
+        assertTrue(dpms.getProfileOwnerAdminLocked(DpmMockContext.CALLER_USER_HANDLE)
+                .ensureUserRestrictions()
+                .getBoolean(UserManager.DISALLOW_OUTGOING_CALLS));
+
+        dpm.clearUserRestriction(admin1, UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES);
+
+        assertFalse(dpms.getProfileOwnerAdminLocked(DpmMockContext.CALLER_USER_HANDLE)
+                .ensureUserRestrictions()
+                .getBoolean(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES));
+        assertTrue(dpms.getProfileOwnerAdminLocked(DpmMockContext.CALLER_USER_HANDLE)
+                .ensureUserRestrictions()
+                .getBoolean(UserManager.DISALLOW_OUTGOING_CALLS));
+
+        dpm.clearUserRestriction(admin1, UserManager.DISALLOW_OUTGOING_CALLS);
+
+        assertFalse(dpms.getProfileOwnerAdminLocked(DpmMockContext.CALLER_USER_HANDLE)
+                .ensureUserRestrictions()
+                .getBoolean(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES));
+        assertFalse(dpms.getProfileOwnerAdminLocked(DpmMockContext.CALLER_USER_HANDLE)
+                .ensureUserRestrictions()
+                .getBoolean(UserManager.DISALLOW_OUTGOING_CALLS));
+
+        // TODO Check inner calls.
+        // TODO Make sure restrictions are written to the file.
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTestable.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTestable.java
index 325bf9f..b80f3bf 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTestable.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTestable.java
@@ -16,6 +16,7 @@
 package com.android.server.devicepolicy;
 
 import android.app.admin.DevicePolicyManager;
+import android.os.UserHandle;
 
 /**
  * Overrides {@link #DevicePolicyManager} for dependency injection.
@@ -31,6 +32,6 @@
 
     @Override
     public int myUserId() {
-        return DpmMockContext.CALLER_USER_HANDLE;
+        return UserHandle.getUserId(dpms.context.binder.callingUid);
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
index 3b30a37..d1b4835 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
@@ -22,11 +22,13 @@
 import android.app.NotificationManager;
 import android.app.backup.IBackupManager;
 import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
 import android.media.IAudioService;
 import android.os.Bundle;
 import android.os.Handler;
@@ -34,6 +36,7 @@
 import android.os.PowerManagerInternal;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.test.mock.MockContentResolver;
 import android.test.mock.MockContext;
 import android.view.IWindowManager;
 
@@ -43,8 +46,10 @@
 import java.util.ArrayList;
 import java.util.List;
 
+import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
 
 /**
  * Context used throughout DPMS tests.
@@ -58,12 +63,12 @@
     /**
      * UID corresponding to {@link #CALLER_USER_HANDLE}.
      */
-    public static final int CALLER_UID = UserHandle.PER_USER_RANGE * CALLER_USER_HANDLE + 123;
+    public static final int CALLER_UID = UserHandle.getUid(CALLER_USER_HANDLE, 20123);
 
     /**
      * UID used when a caller is on the system user.
      */
-    public static final int CALLER_SYSTEM_USER_UID = 123;
+    public static final int CALLER_SYSTEM_USER_UID = 20321;
 
     /**
      * PID of the caller.
@@ -155,6 +160,33 @@
         }
     }
 
+    public static class SettingsForMock {
+        int settingsSecureGetIntForUser(String name, int def, int userHandle) {
+            return 0;
+        }
+
+        void settingsSecurePutIntForUser(String name, int value, int userHandle) {
+        }
+
+        void settingsSecurePutStringForUser(String name, String value, int userHandle) {
+        }
+
+        void settingsGlobalPutStringForUser(String name, String value, int userHandle) {
+        }
+
+        void settingsSecurePutInt(String name, int value) {
+        }
+
+        void settingsGlobalPutInt(String name, int value) {
+        }
+
+        void settingsSecurePutString(String name, String value) {
+        }
+
+        void settingsGlobalPutString(String name, String value) {
+        }
+    }
+
     public final Context realTestContext;
 
     /**
@@ -164,6 +196,9 @@
      */
     public final Context spiedContext;
 
+    public final File dataDir;
+    public final File systemUserDataDir;
+
     public final MockBinder binder;
     public final EnvironmentForMock environment;
     public final SystemPropertiesForMock systemProperties;
@@ -178,14 +213,22 @@
     public final IBackupManager ibackupManager;
     public final IAudioService iaudioService;
     public final LockPatternUtils lockPatternUtils;
+    public final SettingsForMock settings;
+    public final MockContentResolver contentResolver;
 
     /** Note this is a partial mock, not a real mock. */
     public final PackageManager packageManager;
 
     public final List<String> callerPermissions = new ArrayList<>();
 
-    public DpmMockContext(Context context) {
+    private final ArrayList<UserInfo> mUserInfos = new ArrayList<>();
+
+    public DpmMockContext(Context context, File dataDir) {
         realTestContext = context;
+
+        this.dataDir = dataDir;
+        DpmTestUtils.clearDir(dataDir);
+
         binder = new MockBinder();
         environment = mock(EnvironmentForMock.class);
         systemProperties= mock(SystemPropertiesForMock.class);
@@ -200,11 +243,51 @@
         ibackupManager = mock(IBackupManager.class);
         iaudioService = mock(IAudioService.class);
         lockPatternUtils = mock(LockPatternUtils.class);
+        settings = mock(SettingsForMock.class);
 
         // Package manager is huge, so we use a partial mock instead.
         packageManager = spy(context.getPackageManager());
 
         spiedContext = mock(Context.class);
+
+        contentResolver = new MockContentResolver();
+
+        // Add the system user
+        systemUserDataDir = addUser(UserHandle.USER_SYSTEM, UserInfo.FLAG_PRIMARY);
+
+        // System user is always running.
+        setUserRunning(UserHandle.USER_SYSTEM, true);
+    }
+
+    public File addUser(int userId, int flags) {
+
+        // Set up (default) UserInfo for CALLER_USER_HANDLE.
+        final UserInfo uh = new UserInfo(userId, "user" + userId, flags);
+        when(userManager.getUserInfo(eq(userId))).thenReturn(uh);
+
+        mUserInfos.add(uh);
+        when(userManager.getUsers()).thenReturn(mUserInfos);
+
+        // Create a data directory.
+        final File dir = new File(dataDir, "user" + userId);
+        DpmTestUtils.clearDir(dir);
+
+        when(environment.getUserSystemDirectory(eq(userId))).thenReturn(dir);
+        return dir;
+    }
+
+    /**
+     * Add multiple users at once.  They'll all have flag 0.
+     */
+    public void addUsers(int... userIds) {
+        for (int userId : userIds) {
+            addUser(userId, 0);
+        }
+    }
+
+    public void setUserRunning(int userId, boolean isRunning) {
+        when(userManager.isUserRunning(MockUtils.checkUserHandle(userId)))
+                .thenReturn(isRunning);
     }
 
     @Override
@@ -399,4 +482,9 @@
     public void unregisterReceiver(BroadcastReceiver receiver) {
         spiedContext.unregisterReceiver(receiver);
     }
+
+    @Override
+    public ContentResolver getContentResolver() {
+        return contentResolver;
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java
index 6f9f6ab..63bf125 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java
@@ -34,10 +34,9 @@
         super.setUp();
 
         mRealTestContext = super.getContext();
-        mMockContext = new DpmMockContext(super.getContext());
 
-        dataDir = new File(mRealTestContext.getCacheDir(), "test-data");
-        DpmTestUtils.clearDir(dataDir);
+        mMockContext = new DpmMockContext(
+                mRealTestContext, new File(mRealTestContext.getCacheDir(), "test-data"));
     }
 
     @Override
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestUtils.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestUtils.java
index 44a851a..7506273 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestUtils.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestUtils.java
@@ -17,6 +17,8 @@
 package com.android.server.devicepolicy;
 
 import android.os.FileUtils;
+import android.os.Parcel;
+import android.os.Parcelable;
 import android.util.Log;
 import android.util.Printer;
 
@@ -41,6 +43,15 @@
         return list == null ? 0 : list.size();
     }
 
+    public static <T extends Parcelable> T cloneParcelable(T source) {
+        Parcel p = Parcel.obtain();
+        p.writeParcelable(source, 0);
+        p.setDataPosition(0);
+        final T clone = p.readParcelable(DpmTestUtils.class.getClassLoader());
+        p.recycle();
+        return clone;
+    }
+
     public static Printer LOG_PRINTER = new Printer() {
         @Override
         public void println(String x) {
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/OwnersTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/OwnersTest.java
index a07d615..79845d2 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/OwnersTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/OwnersTest.java
@@ -21,6 +21,7 @@
 import android.content.ComponentName;
 import android.content.pm.UserInfo;
 import android.os.UserHandle;
+import android.os.UserManager;
 import android.util.Log;
 
 import java.io.BufferedReader;
@@ -28,6 +29,8 @@
 import java.io.FileWriter;
 import java.io.IOException;
 import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.io.StringWriter;
 import java.util.ArrayList;
 
 import static org.mockito.Mockito.when;
@@ -69,22 +72,12 @@
         }
     }
 
-    private void addUsersToUserManager(int... userIds) {
-        final ArrayList<UserInfo> userInfos = new ArrayList<>();
-        for (int userId : userIds) {
-            final UserInfo ui = new UserInfo();
-            ui.id = userId;
-            userInfos.add(ui);
-        }
-        when(getContext().userManager.getUsers()).thenReturn(userInfos);
-    }
-
     public void testUpgrade01() throws Exception {
-        addUsersToUserManager(10, 11, 20, 21);
+        getContext().addUsers(10, 11, 20, 21);
 
         // First, migrate.
         {
-            final OwnersTestable owners = new OwnersTestable(getContext(), dataDir);
+            final OwnersTestable owners = new OwnersTestable(getContext());
 
             createLegacyFile(owners.getLegacyConfigFileWithTestOverride(),
                     readAsset("OwnersTest/test01/input.xml"));
@@ -104,30 +97,28 @@
 
             assertFalse(owners.hasDeviceOwner());
             assertEquals(UserHandle.USER_NULL, owners.getDeviceOwnerUserId());
-            assertFalse(owners.hasDeviceInitializer());
             assertNull(owners.getSystemUpdatePolicy());
             assertEquals(0, owners.getProfileOwnerKeys().size());
         }
 
         // Then re-read and check.
         {
-            final OwnersTestable owners = new OwnersTestable(getContext(), dataDir);
+            final OwnersTestable owners = new OwnersTestable(getContext());
             owners.load();
 
             assertFalse(owners.hasDeviceOwner());
             assertEquals(UserHandle.USER_NULL, owners.getDeviceOwnerUserId());
-            assertFalse(owners.hasDeviceInitializer());
             assertNull(owners.getSystemUpdatePolicy());
             assertEquals(0, owners.getProfileOwnerKeys().size());
         }
     }
 
     public void testUpgrade02() throws Exception {
-        addUsersToUserManager(10, 11, 20, 21);
+        getContext().addUsers(10, 11, 20, 21);
 
         // First, migrate.
         {
-            final OwnersTestable owners = new OwnersTestable(getContext(), dataDir);
+            final OwnersTestable owners = new OwnersTestable(getContext());
 
             createLegacyFile(owners.getLegacyConfigFileWithTestOverride(),
                     readAsset("OwnersTest/test02/input.xml"));
@@ -149,14 +140,13 @@
             assertEquals("com.google.android.testdpc", owners.getDeviceOwnerPackageName());
             assertEquals(UserHandle.USER_SYSTEM, owners.getDeviceOwnerUserId());
 
-            assertFalse(owners.hasDeviceInitializer());
             assertNull(owners.getSystemUpdatePolicy());
             assertEquals(0, owners.getProfileOwnerKeys().size());
         }
 
         // Then re-read and check.
         {
-            final OwnersTestable owners = new OwnersTestable(getContext(), dataDir);
+            final OwnersTestable owners = new OwnersTestable(getContext());
             owners.load();
 
             assertTrue(owners.hasDeviceOwner());
@@ -164,18 +154,17 @@
             assertEquals("com.google.android.testdpc", owners.getDeviceOwnerPackageName());
             assertEquals(UserHandle.USER_SYSTEM, owners.getDeviceOwnerUserId());
 
-            assertFalse(owners.hasDeviceInitializer());
             assertNull(owners.getSystemUpdatePolicy());
             assertEquals(0, owners.getProfileOwnerKeys().size());
         }
     }
 
     public void testUpgrade03() throws Exception {
-        addUsersToUserManager(10, 11, 20, 21);
+        getContext().addUsers(10, 11, 20, 21);
 
         // First, migrate.
         {
-            final OwnersTestable owners = new OwnersTestable(getContext(), dataDir);
+            final OwnersTestable owners = new OwnersTestable(getContext());
 
             createLegacyFile(owners.getLegacyConfigFileWithTestOverride(),
                     readAsset("OwnersTest/test03/input.xml"));
@@ -194,7 +183,6 @@
 
             assertFalse(owners.hasDeviceOwner());
             assertEquals(UserHandle.USER_NULL, owners.getDeviceOwnerUserId());
-            assertFalse(owners.hasDeviceInitializer());
             assertNull(owners.getSystemUpdatePolicy());
 
             assertEquals(2, owners.getProfileOwnerKeys().size());
@@ -212,12 +200,11 @@
 
         // Then re-read and check.
         {
-            final OwnersTestable owners = new OwnersTestable(getContext(), dataDir);
+            final OwnersTestable owners = new OwnersTestable(getContext());
             owners.load();
 
             assertFalse(owners.hasDeviceOwner());
             assertEquals(UserHandle.USER_NULL, owners.getDeviceOwnerUserId());
-            assertFalse(owners.hasDeviceInitializer());
             assertNull(owners.getSystemUpdatePolicy());
 
             assertEquals(2, owners.getProfileOwnerKeys().size());
@@ -235,11 +222,11 @@
     }
 
     public void testUpgrade04() throws Exception {
-        addUsersToUserManager(10, 11, 20, 21);
+        getContext().addUsers(10, 11, 20, 21);
 
         // First, migrate.
         {
-            final OwnersTestable owners = new OwnersTestable(getContext(), dataDir);
+            final OwnersTestable owners = new OwnersTestable(getContext());
 
             createLegacyFile(owners.getLegacyConfigFileWithTestOverride(),
                     readAsset("OwnersTest/test04/input.xml"));
@@ -261,8 +248,6 @@
             assertEquals("com.google.android.testdpc", owners.getDeviceOwnerPackageName());
             assertEquals(UserHandle.USER_SYSTEM, owners.getDeviceOwnerUserId());
 
-            assertTrue(owners.hasDeviceInitializer());
-            assertEquals("com.google.android.testdpcx", owners.getDeviceInitializerPackageName());
             assertNotNull(owners.getSystemUpdatePolicy());
             assertEquals(5, owners.getSystemUpdatePolicy().getPolicyType());
 
@@ -281,7 +266,7 @@
 
         // Then re-read and check.
         {
-            final OwnersTestable owners = new OwnersTestable(getContext(), dataDir);
+            final OwnersTestable owners = new OwnersTestable(getContext());
             owners.load();
 
             assertTrue(owners.hasDeviceOwner());
@@ -289,8 +274,6 @@
             assertEquals("com.google.android.testdpc", owners.getDeviceOwnerPackageName());
             assertEquals(UserHandle.USER_SYSTEM, owners.getDeviceOwnerUserId());
 
-            assertTrue(owners.hasDeviceInitializer());
-            assertEquals("com.google.android.testdpcx", owners.getDeviceInitializerPackageName());
             assertNotNull(owners.getSystemUpdatePolicy());
             assertEquals(5, owners.getSystemUpdatePolicy().getPolicyType());
 
@@ -309,11 +292,11 @@
     }
 
     public void testUpgrade05() throws Exception {
-        addUsersToUserManager(10, 11, 20, 21);
+        getContext().addUsers(10, 11, 20, 21);
 
         // First, migrate.
         {
-            final OwnersTestable owners = new OwnersTestable(getContext(), dataDir);
+            final OwnersTestable owners = new OwnersTestable(getContext());
 
             createLegacyFile(owners.getLegacyConfigFileWithTestOverride(),
                     readAsset("OwnersTest/test05/input.xml"));
@@ -323,7 +306,8 @@
             // The legacy file should be removed.
             assertFalse(owners.getLegacyConfigFileWithTestOverride().exists());
 
-            assertTrue(owners.getDeviceOwnerFileWithTestOverride().exists());
+            // Note device initializer is no longer supported.  No need to write the DO file.
+            assertFalse(owners.getDeviceOwnerFileWithTestOverride().exists());
 
             assertFalse(owners.getProfileOwnerFileWithTestOverride(10).exists());
             assertFalse(owners.getProfileOwnerFileWithTestOverride(11).exists());
@@ -332,8 +316,6 @@
             assertFalse(owners.hasDeviceOwner());
             assertEquals(UserHandle.USER_NULL, owners.getDeviceOwnerUserId());
 
-            assertTrue(owners.hasDeviceInitializer());
-            assertEquals("com.google.android.testdpcx", owners.getDeviceInitializerPackageName());
 
             assertNull(owners.getSystemUpdatePolicy());
             assertEquals(0, owners.getProfileOwnerKeys().size());
@@ -341,14 +323,12 @@
 
         // Then re-read and check.
         {
-            final OwnersTestable owners = new OwnersTestable(getContext(), dataDir);
+            final OwnersTestable owners = new OwnersTestable(getContext());
             owners.load();
 
             assertFalse(owners.hasDeviceOwner());
             assertEquals(UserHandle.USER_NULL, owners.getDeviceOwnerUserId());
 
-            assertTrue(owners.hasDeviceInitializer());
-            assertEquals("com.google.android.testdpcx", owners.getDeviceInitializerPackageName());
 
             assertNull(owners.getSystemUpdatePolicy());
             assertEquals(0, owners.getProfileOwnerKeys().size());
@@ -356,11 +336,11 @@
     }
 
     public void testUpgrade06() throws Exception {
-        addUsersToUserManager(10, 11, 20, 21);
+        getContext().addUsers(10, 11, 20, 21);
 
         // First, migrate.
         {
-            final OwnersTestable owners = new OwnersTestable(getContext(), dataDir);
+            final OwnersTestable owners = new OwnersTestable(getContext());
 
             createLegacyFile(owners.getLegacyConfigFileWithTestOverride(),
                     readAsset("OwnersTest/test06/input.xml"));
@@ -378,7 +358,6 @@
 
             assertFalse(owners.hasDeviceOwner());
             assertEquals(UserHandle.USER_NULL, owners.getDeviceOwnerUserId());
-            assertFalse(owners.hasDeviceInitializer());
             assertEquals(0, owners.getProfileOwnerKeys().size());
 
             assertNotNull(owners.getSystemUpdatePolicy());
@@ -387,12 +366,11 @@
 
         // Then re-read and check.
         {
-            final OwnersTestable owners = new OwnersTestable(getContext(), dataDir);
+            final OwnersTestable owners = new OwnersTestable(getContext());
             owners.load();
 
             assertFalse(owners.hasDeviceOwner());
             assertEquals(UserHandle.USER_NULL, owners.getDeviceOwnerUserId());
-            assertFalse(owners.hasDeviceInitializer());
             assertEquals(0, owners.getProfileOwnerKeys().size());
 
             assertNotNull(owners.getSystemUpdatePolicy());
@@ -401,9 +379,9 @@
     }
 
     public void testRemoveExistingFiles() throws Exception {
-        addUsersToUserManager(10, 11, 20, 21);
+        getContext().addUsers(10, 11, 20, 21);
 
-        final OwnersTestable owners = new OwnersTestable(getContext(), dataDir);
+        final OwnersTestable owners = new OwnersTestable(getContext());
 
         // First, migrate to create new-style config files.
         createLegacyFile(owners.getLegacyConfigFileWithTestOverride(),
@@ -418,7 +396,6 @@
         assertTrue(owners.getProfileOwnerFileWithTestOverride(11).exists());
 
         // Then clear all information and save.
-        owners.clearDeviceInitializer();
         owners.clearDeviceOwner();
         owners.clearSystemUpdatePolicy();
         owners.removeProfileOwner(10);
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 7f813ec..3160e39 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -753,7 +753,7 @@
                                         UsbManager.USB_FUNCTION_MTP)
                                 || UsbManager.containsFunction(mCurrentFunctions,
                                         UsbManager.USB_FUNCTION_PTP);
-                        if (active && mCurrentUser != UserHandle.USER_NULL) {
+                        if (mUsbDataUnlocked && active && mCurrentUser != UserHandle.USER_NULL) {
                             Slog.v(TAG, "Current user switched to " + mCurrentUser
                                     + "; resetting USB host stack for MTP or PTP");
                             setEnabledFunctions(mCurrentFunctions, true);
diff --git a/telecomm/java/android/telecom/PhoneAccount.java b/telecomm/java/android/telecom/PhoneAccount.java
index cdb0bf2..5c64168 100644
--- a/telecomm/java/android/telecom/PhoneAccount.java
+++ b/telecomm/java/android/telecom/PhoneAccount.java
@@ -28,6 +28,7 @@
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.Icon;
 import android.net.Uri;
+import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.TextUtils;
@@ -160,6 +161,7 @@
     private final CharSequence mShortDescription;
     private final List<String> mSupportedUriSchemes;
     private final Icon mIcon;
+    private final Bundle mExtras;
     private boolean mIsEnabled;
 
     /**
@@ -175,6 +177,7 @@
         private CharSequence mShortDescription;
         private List<String> mSupportedUriSchemes = new ArrayList<String>();
         private Icon mIcon;
+        private Bundle mExtras;
         private boolean mIsEnabled = false;
 
         /**
@@ -300,6 +303,20 @@
         }
 
         /**
+         * Specifies the extras associated with the {@link PhoneAccount}.
+         * <p>
+         * {@code PhoneAccount}s only support extra values of type: {@link String}, {@link Integer},
+         * and {@link Boolean}.  Extras which are not of these types are ignored.
+         *
+         * @param extras
+         * @return
+         */
+        public Builder setExtras(Bundle extras) {
+            mExtras = extras;
+            return this;
+        }
+
+        /**
          * Sets the enabled state of the phone account.
          *
          * @param isEnabled The enabled state.
@@ -332,6 +349,7 @@
                     mLabel,
                     mShortDescription,
                     mSupportedUriSchemes,
+                    mExtras,
                     mIsEnabled);
         }
     }
@@ -346,6 +364,7 @@
             CharSequence label,
             CharSequence shortDescription,
             List<String> supportedUriSchemes,
+            Bundle extras,
             boolean isEnabled) {
         mAccountHandle = account;
         mAddress = address;
@@ -356,6 +375,7 @@
         mLabel = label;
         mShortDescription = shortDescription;
         mSupportedUriSchemes = Collections.unmodifiableList(supportedUriSchemes);
+        mExtras = extras;
         mIsEnabled = isEnabled;
     }
 
@@ -455,6 +475,18 @@
     }
 
     /**
+     * The extras associated with this {@code PhoneAccount}.
+     * <p>
+     * A {@link ConnectionService} may provide implementation specific information about the
+     * {@link PhoneAccount} via the extras.
+     *
+     * @return The extras.
+     */
+    public Bundle getExtras() {
+        return mExtras;
+    }
+
+    /**
      * The icon to represent this {@code PhoneAccount}.
      *
      * @return The icon.
@@ -552,6 +584,8 @@
             out.writeInt(1);
             mIcon.writeToParcel(out, flags);
         }
+
+        out.writeBundle(mExtras);
         out.writeByte((byte) (mIsEnabled ? 1 : 0));
     }
 
@@ -594,6 +628,7 @@
         } else {
             mIcon = null;
         }
+        mExtras = in.readBundle();
         mIsEnabled = in.readByte() == 1;
     }
 
@@ -610,6 +645,8 @@
             sb.append(scheme)
                     .append(" ");
         }
+        sb.append(" Extras : ");
+        sb.append(mExtras);
         sb.append("]");
         return sb.toString();
     }
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index e57964a..ba91254 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -304,6 +304,31 @@
             "carrier_instant_lettering_escaped_chars_string";
 
     /**
+     * When IMS instant lettering is available for a carrier (see
+     * {@link #KEY_CARRIER_INSTANT_LETTERING_AVAILABLE_BOOL}), determines the character encoding
+     * which will be used when determining the length of messages.  Used in the InCall UI to limit
+     * the number of characters the user may type.  If empty-string, the instant lettering
+     * message size limit will be enforced on a 1:1 basis.  That is, each character will count
+     * towards the messages size limit as a single bye.  If a character encoding is specified, the
+     * message size limit will be based on the number of bytes in the message per the specified
+     * encoding.
+     * @hide
+     */
+    public static final String KEY_CARRIER_INSTANT_LETTERING_ENCODING_STRING =
+            "carrier_instant_lettering_encoding_string";
+
+    /**
+     * When IMS instant lettering is available for a carrier (see
+     * {@link #KEY_CARRIER_INSTANT_LETTERING_AVAILABLE_BOOL}), the length limit for messages.  Used
+     * in the InCall UI to ensure the user cannot enter more characters than allowed by the carrier.
+     * See also {@link #KEY_CARRIER_INSTANT_LETTERING_ENCODING_STRING} for more information on how
+     * the length of the message is calculated.
+     * @hide
+     */
+    public static final String KEY_CARRIER_INSTANT_LETTERING_LENGTH_LIMIT_INT =
+            "carrier_instant_lettering_length_limit_int";
+
+    /**
      * If Voice Radio Technology is RIL_RADIO_TECHNOLOGY_LTE:14 or RIL_RADIO_TECHNOLOGY_UNKNOWN:0
      * this is the value that should be used instead. A configuration value of
      * RIL_RADIO_TECHNOLOGY_UNKNOWN:0 means there is no replacement value and that the default
@@ -508,6 +533,8 @@
         sDefaults.putBoolean(KEY_CARRIER_USE_IMS_FIRST_FOR_EMERGENCY_BOOL, true);
         sDefaults.putString(KEY_CARRIER_INSTANT_LETTERING_INVALID_CHARS_STRING, "");
         sDefaults.putString(KEY_CARRIER_INSTANT_LETTERING_ESCAPED_CHARS_STRING, "");
+        sDefaults.putString(KEY_CARRIER_INSTANT_LETTERING_ENCODING_STRING, "");
+        sDefaults.putInt(KEY_CARRIER_INSTANT_LETTERING_LENGTH_LIMIT_INT, 64);
         sDefaults.putBoolean(KEY_DISABLE_CDMA_ACTIVATION_CODE_BOOL, false);
         sDefaults.putBoolean(KEY_DTMF_TYPE_ENABLED_BOOL, false);
         sDefaults.putBoolean(KEY_ENABLE_DIALER_KEY_VIBRATION_BOOL, true);
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/MultiProducerActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/MultiProducerActivity.java
index b458d9b..7628c5c 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/MultiProducerActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/MultiProducerActivity.java
@@ -188,7 +188,7 @@
                 // This call should be done while the rendernode's displaylist is produced.
                 // For simplicity of this test we do this before we kick off the draw.
                 mContent.getLocationInSurface(surfaceOrigin);
-                mRenderer.setContentOverdrawProtectionBounds(surfaceOrigin[0], surfaceOrigin[1],
+                mRenderer.setContentDrawBounds(surfaceOrigin[0], surfaceOrigin[1],
                         surfaceOrigin[0] + mContent.getWidth(),
                         surfaceOrigin[1] + mContent.getHeight());
                 // Determine new position for frame.
diff --git a/tests/SurfaceComposition/src/android/surfacecomposition/SurfaceCompositionMeasuringActivity.java b/tests/SurfaceComposition/src/android/surfacecomposition/SurfaceCompositionMeasuringActivity.java
index e3e1d34..b4e0c70 100644
--- a/tests/SurfaceComposition/src/android/surfacecomposition/SurfaceCompositionMeasuringActivity.java
+++ b/tests/SurfaceComposition/src/android/surfacecomposition/SurfaceCompositionMeasuringActivity.java
@@ -106,12 +106,12 @@
 
     class CompositorScore {
         double mSurfaces;
-        double mBitrate;
+        double mBandwidth;
 
         @Override
         public String toString() {
             return DOUBLE_FORMAT.format(mSurfaces) + " surfaces. " +
-                    "Bitrate: " + getReadableMemory((long)mBitrate) + "/s";
+                    "Bandwidth: " + getReadableMemory((long)mBandwidth) + "/s";
         }
     }
 
@@ -131,7 +131,7 @@
         score.mSurfaces = measureCompositionScore(new Measurement(0, 60.0),
                 new Measurement(mViews.size() + 1, 0.0f), pixelFormat);
         // Assume 32 bits per pixel.
-        score.mBitrate = score.mSurfaces * mTargetFPS * mWidth * mHeight * 4.0;
+        score.mBandwidth = score.mSurfaces * mTargetFPS * mWidth * mHeight * 4.0;
         //memAccessTask.stop();
         return score;
     }
diff --git a/tests/SurfaceComposition/src/android/surfacecomposition/SurfaceCompositionTest.java b/tests/SurfaceComposition/src/android/surfacecomposition/SurfaceCompositionTest.java
index 6e9e739..3f04888 100644
--- a/tests/SurfaceComposition/src/android/surfacecomposition/SurfaceCompositionTest.java
+++ b/tests/SurfaceComposition/src/android/surfacecomposition/SurfaceCompositionTest.java
@@ -15,7 +15,9 @@
  */
 package android.surfacecomposition;
 
+import android.app.Activity;
 import android.graphics.PixelFormat;
+import android.os.Bundle;
 import android.surfacecomposition.SurfaceCompositionMeasuringActivity.AllocationScore;
 import android.surfacecomposition.SurfaceCompositionMeasuringActivity.CompositorScore;
 import android.test.ActivityInstrumentationTestCase2;
@@ -25,6 +27,16 @@
 public class SurfaceCompositionTest extends
         ActivityInstrumentationTestCase2<SurfaceCompositionMeasuringActivity> {
     private final static String TAG = "SurfaceCompositionTest";
+    private final static String KEY_SURFACE_COMPOSITION_PERFORMANCE =
+            "surface-compoistion-peformance-sps";
+    private final static String KEY_SURFACE_COMPOSITION_BANDWITH =
+            "surface-compoistion-bandwidth-gbps";
+    private final static String KEY_SURFACE_ALLOCATION_PERFORMANCE_MEDIAN =
+            "surface-allocation-performance-median-sps";
+    private final static String KEY_SURFACE_ALLOCATION_PERFORMANCE_MIN =
+            "surface-allocation-performance-min-sps";
+    private final static String KEY_SURFACE_ALLOCATION_PERFORMANCE_MAX =
+            "surface-allocation-performance-max-sps";
 
     // Pass threshold for major pixel formats.
     private final static int[] TEST_PIXEL_FORMATS = new int[] {
@@ -53,6 +65,7 @@
 
     @SmallTest
     public void testSurfaceCompositionPerformance() {
+        Bundle status = new Bundle();
         for (int i = 0; i < TEST_PIXEL_FORMATS.length; ++i) {
             int pixelFormat = TEST_PIXEL_FORMATS[i];
             String formatName = SurfaceCompositionMeasuringActivity.getPixelFormatInfo(pixelFormat);
@@ -62,11 +75,20 @@
                     "performance score. " + score.mSurfaces + " < " +
                     MIN_ACCEPTED_COMPOSITION_SCORE[i] + ".",
                     score.mSurfaces >= MIN_ACCEPTED_COMPOSITION_SCORE[i]);
+            // Send status only for TRANSLUCENT format.
+            if (pixelFormat == PixelFormat.TRANSLUCENT) {
+                status.putDouble(KEY_SURFACE_COMPOSITION_PERFORMANCE, score.mSurfaces);
+                // Put bandwidth in GBPS.
+                status.putDouble(KEY_SURFACE_COMPOSITION_BANDWITH, score.mBandwidth /
+                        (1024.0 * 1024.0 * 1024.0));
+            }
         }
+        getInstrumentation().sendStatus(Activity.RESULT_OK, status);
     }
 
     @SmallTest
     public void testSurfaceAllocationPerformance() {
+        Bundle status = new Bundle();
         for (int i = 0; i < TEST_PIXEL_FORMATS.length; ++i) {
             int pixelFormat = TEST_PIXEL_FORMATS[i];
             String formatName = SurfaceCompositionMeasuringActivity.getPixelFormatInfo(pixelFormat);
@@ -76,6 +98,13 @@
                     "performance score. " + score.mMedian + " < " +
                     MIN_ACCEPTED_ALLOCATION_SCORE[i] + ".",
                     score.mMedian >= MIN_ACCEPTED_ALLOCATION_SCORE[i]);
+            // Send status only for TRANSLUCENT format.
+            if (pixelFormat == PixelFormat.TRANSLUCENT) {
+                status.putDouble(KEY_SURFACE_ALLOCATION_PERFORMANCE_MEDIAN, score.mMedian);
+                status.putDouble(KEY_SURFACE_ALLOCATION_PERFORMANCE_MIN, score.mMin);
+                status.putDouble(KEY_SURFACE_ALLOCATION_PERFORMANCE_MAX, score.mMax);
+            }
         }
+        getInstrumentation().sendStatus(Activity.RESULT_OK, status);
     }
 }
diff --git a/tools/layoutlib/bridge/src/android/animation/AnimatorInflater_Delegate.java b/tools/layoutlib/bridge/src/android/animation/AnimatorInflater_Delegate.java
deleted file mode 100644
index 4475fa4..0000000
--- a/tools/layoutlib/bridge/src/android/animation/AnimatorInflater_Delegate.java
+++ /dev/null
@@ -1,59 +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;
-
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.content.res.Resources.NotFoundException;
-import android.content.res.Resources.Theme;
-import android.util.AttributeSet;
-
-/**
- * Delegate providing alternate implementation to static methods in {@link AnimatorInflater}.
- */
-public class AnimatorInflater_Delegate {
-
-    @LayoutlibDelegate
-    /*package*/ static Animator loadAnimator(Context context, int id)
-            throws NotFoundException {
-        return loadAnimator(context.getResources(), context.getTheme(), id);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static Animator loadAnimator(Resources resources, Theme theme, int id)
-            throws NotFoundException {
-        return loadAnimator(resources, theme, id, 1);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static Animator loadAnimator(Resources resources, Theme theme, int id,
-            float pathErrorScale) throws NotFoundException {
-        // This is a temporary fix to http://b.android.com/77865. This skips loading the
-        // animation altogether.
-        // TODO: Remove this override when Path.approximate() is supported.
-        return new FakeAnimator();
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static ValueAnimator loadAnimator(Resources res, Theme theme,
-            AttributeSet attrs, ValueAnimator anim, float pathErrorScale)
-            throws NotFoundException {
-        return AnimatorInflater.loadAnimator_Original(res, theme, attrs, anim, pathErrorScale);
-    }
-}
diff --git a/tools/layoutlib/bridge/src/android/content/res/BridgeResources.java b/tools/layoutlib/bridge/src/android/content/res/BridgeResources.java
index f4b1f2c..0e39243 100644
--- a/tools/layoutlib/bridge/src/android/content/res/BridgeResources.java
+++ b/tools/layoutlib/bridge/src/android/content/res/BridgeResources.java
@@ -18,6 +18,7 @@
 
 import com.android.SdkConstants;
 import com.android.ide.common.rendering.api.ArrayResourceValue;
+import com.android.ide.common.rendering.api.DensityBasedResourceValue;
 import com.android.ide.common.rendering.api.LayoutLog;
 import com.android.ide.common.rendering.api.LayoutlibCallback;
 import com.android.ide.common.rendering.api.ResourceValue;
@@ -661,13 +662,18 @@
         Pair<String, ResourceValue> value = getResourceValue(id, mPlatformResourceFlag);
 
         if (value != null) {
-            String v = value.getSecond().getValue();
+            ResourceValue resVal = value.getSecond();
+            String v = resVal.getValue();
 
             if (v != null) {
                 if (ResourceHelper.parseFloatAttribute(value.getFirst(), v, outValue,
                         false /*requireUnit*/)) {
                     return;
                 }
+                if (resVal instanceof DensityBasedResourceValue) {
+                    outValue.density =
+                      ((DensityBasedResourceValue) resVal).getResourceDensity().getDpiValue();
+                }
 
                 // else it's a string
                 outValue.type = TypedValue.TYPE_STRING;
diff --git a/tools/layoutlib/bridge/src/android/graphics/BitmapFactory_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/BitmapFactory_Delegate.java
index d858953..60514b6 100644
--- a/tools/layoutlib/bridge/src/android/graphics/BitmapFactory_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/BitmapFactory_Delegate.java
@@ -59,6 +59,7 @@
             if (opts.inPremultiplied) {
                 bitmapCreateFlags.add(BitmapCreateFlags.PREMULTIPLIED);
             }
+            opts.inScaled = false;
         }
 
         try {
diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
index f8b3739..64cd503 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
@@ -35,6 +35,8 @@
 import java.awt.Shape;
 import java.awt.geom.AffineTransform;
 import java.awt.geom.Arc2D;
+import java.awt.geom.Path2D;
+import java.awt.geom.Rectangle2D;
 import java.awt.image.BufferedImage;
 
 
@@ -707,6 +709,12 @@
                     @Override
                     public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) {
                         Shape shape = pathDelegate.getJavaShape();
+                        Rectangle2D bounds = shape.getBounds2D();
+                        if (bounds.isEmpty()) {
+                            // Apple JRE 1.6 doesn't like drawing empty shapes.
+                            // http://b.android.com/178278
+                            return;
+                        }
                         int style = paintDelegate.getStyle();
 
                         if (style == Paint.Style.FILL.nativeInt ||
diff --git a/tools/layoutlib/bridge/src/android/graphics/PathMeasure_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/PathMeasure_Delegate.java
new file mode 100644
index 0000000..dd2978f
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/PathMeasure_Delegate.java
@@ -0,0 +1,210 @@
+/*
+ * 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.graphics;
+
+import com.android.ide.common.rendering.api.LayoutLog;
+import com.android.layoutlib.bridge.Bridge;
+import com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import java.awt.geom.PathIterator;
+import java.awt.geom.Point2D;
+
+/**
+ * Delegate implementing the native methods of {@link android.graphics.PathMeasure}
+ * <p/>
+ * Through the layoutlib_create tool, the original native methods of PathMeasure have been
+ * replaced by
+ * calls to methods of the same name in this delegate class.
+ * <p/>
+ * This class behaves like the original native implementation, but in Java, keeping previously
+ * native data into its own objects and mapping them to int that are sent back and forth between it
+ * and the original PathMeasure class.
+ *
+ * @see DelegateManager
+ */
+public final class PathMeasure_Delegate {
+    // ---- delegate manager ----
+    private static final DelegateManager<PathMeasure_Delegate> sManager =
+            new DelegateManager<PathMeasure_Delegate>(PathMeasure_Delegate.class);
+
+    // ---- delegate data ----
+    // This governs how accurate the approximation of the Path is.
+    private static final float PRECISION = 0.002f;
+
+    /**
+     * Array containing the path points components. There are three components for each point:
+     * <ul>
+     *     <li>Fraction along the length of the path that the point resides</li>
+     *     <li>The x coordinate of the point</li>
+     *     <li>The y coordinate of the point</li>
+     * </ul>
+     */
+    private float mPathPoints[];
+    private long mNativePath;
+
+    private PathMeasure_Delegate(long native_path, boolean forceClosed) {
+        mNativePath = native_path;
+        if (forceClosed && mNativePath != 0) {
+            // Copy the path and call close
+            mNativePath = Path_Delegate.init2(native_path);
+            Path_Delegate.native_close(mNativePath);
+        }
+
+        mPathPoints =
+                mNativePath != 0 ? Path_Delegate.native_approximate(mNativePath, PRECISION) : null;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static long native_create(long native_path, boolean forceClosed) {
+        return sManager.addNewDelegate(new PathMeasure_Delegate(native_path, forceClosed));
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_destroy(long native_instance) {
+        sManager.removeJavaReferenceFor(native_instance);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static boolean native_getPosTan(long native_instance, float distance, float pos[],
+            float tan[]) {
+        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+                "PathMeasure.getPostTan is not supported.", null, null);
+        return false;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static boolean native_getMatrix(long native_instance, float distance, long
+            native_matrix, int flags) {
+        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+                "PathMeasure.getMatrix is not supported.", null, null);
+        return false;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static boolean native_nextContour(long native_instance) {
+        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+                "PathMeasure.nextContour is not supported.", null, null);
+        return false;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_setPath(long native_instance, long native_path, boolean
+            forceClosed) {
+        PathMeasure_Delegate pathMeasure = sManager.getDelegate(native_instance);
+        assert pathMeasure != null;
+
+        if (forceClosed && native_path != 0) {
+            // Copy the path and call close
+            native_path = Path_Delegate.init2(native_path);
+            Path_Delegate.native_close(native_path);
+        }
+        pathMeasure.mNativePath = native_path;
+        pathMeasure.mPathPoints = Path_Delegate.native_approximate(native_path, PRECISION);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static float native_getLength(long native_instance) {
+        PathMeasure_Delegate pathMeasure = sManager.getDelegate(native_instance);
+        assert pathMeasure != null;
+
+        if (pathMeasure.mPathPoints == null) {
+            return 0;
+        }
+
+        float length = 0;
+        int nPoints = pathMeasure.mPathPoints.length / 3;
+        for (int i = 1; i < nPoints; i++) {
+            length += Point2D.distance(
+                    pathMeasure.mPathPoints[(i - 1) * 3 + 1],
+                    pathMeasure.mPathPoints[(i - 1) * 3 + 2],
+                    pathMeasure.mPathPoints[i*3 + 1],
+                    pathMeasure.mPathPoints[i*3 + 2]);
+        }
+
+        return length;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static boolean native_isClosed(long native_instance) {
+        PathMeasure_Delegate pathMeasure = sManager.getDelegate(native_instance);
+        assert pathMeasure != null;
+
+        Path_Delegate path = Path_Delegate.getDelegate(pathMeasure.mNativePath);
+        if (path == null) {
+            return false;
+        }
+
+        PathIterator pathIterator = path.getJavaShape().getPathIterator(null);
+
+        int type = 0;
+        float segment[] = new float[6];
+        while (!pathIterator.isDone()) {
+            type = pathIterator.currentSegment(segment);
+            pathIterator.next();
+        }
+
+        // A path is a closed path if the last element is SEG_CLOSE
+        return type == PathIterator.SEG_CLOSE;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static boolean native_getSegment(long native_instance, float startD, float stopD,
+            long native_dst_path, boolean startWithMoveTo) {
+        if (startD < 0) {
+            startD = 0;
+        }
+
+        if (startD >= stopD) {
+            return false;
+        }
+
+        PathMeasure_Delegate pathMeasure = sManager.getDelegate(native_instance);
+        assert pathMeasure != null;
+
+        if (pathMeasure.mPathPoints == null) {
+            return false;
+        }
+
+        float accLength = 0;
+        boolean isZeroLength = true; // Whether the output has zero length or not
+        int nPoints = pathMeasure.mPathPoints.length / 3;
+        for (int i = 0; i < nPoints; i++) {
+            float x = pathMeasure.mPathPoints[i * 3 + 1];
+            float y = pathMeasure.mPathPoints[i * 3 + 2];
+            if (accLength >= startD && accLength <= stopD) {
+                if (startWithMoveTo) {
+                    startWithMoveTo = false;
+                    Path_Delegate.native_moveTo(native_dst_path, x, y);
+                } else {
+                    isZeroLength = false;
+                    Path_Delegate.native_lineTo(native_dst_path, x, y);
+                }
+            }
+
+            if (i > 0) {
+                accLength += Point2D.distance(
+                        pathMeasure.mPathPoints[(i - 1) * 3 + 1],
+                        pathMeasure.mPathPoints[(i - 1) * 3 + 2],
+                        pathMeasure.mPathPoints[i * 3 + 1],
+                        pathMeasure.mPathPoints[i * 3 + 2]);
+            }
+        }
+
+        return !isZeroLength;
+    }
+}
diff --git a/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java
index 3c9a062..a2a53fe 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java
@@ -36,6 +36,7 @@
 import java.awt.geom.Point2D;
 import java.awt.geom.Rectangle2D;
 import java.awt.geom.RoundRectangle2D;
+import java.util.ArrayList;
 
 /**
  * Delegate implementing the native methods of android.graphics.Path
@@ -173,11 +174,8 @@
     @LayoutlibDelegate
     /*package*/ static boolean native_isEmpty(long nPath) {
         Path_Delegate pathDelegate = sManager.getDelegate(nPath);
-        if (pathDelegate == null) {
-            return true;
-        }
+        return pathDelegate == null || pathDelegate.isEmpty();
 
-        return pathDelegate.isEmpty();
     }
 
     @LayoutlibDelegate
@@ -488,54 +486,44 @@
 
     @LayoutlibDelegate
     /*package*/ static float[] native_approximate(long nPath, float error) {
-        Bridge.getLog().warning(LayoutLog.TAG_UNSUPPORTED, "Path.approximate() not fully supported",
-                null);
         Path_Delegate pathDelegate = sManager.getDelegate(nPath);
         if (pathDelegate == null) {
             return null;
         }
-        PathIterator pathIterator = pathDelegate.mPath.getPathIterator(null);
-        float[] tmp = new float[6];
-        float[] coords = new float[6];
-        boolean isFirstPoint = true;
-        while (!pathIterator.isDone()) {
-            int type = pathIterator.currentSegment(tmp);
-            switch (type) {
-                case PathIterator.SEG_MOVETO:
-                case PathIterator.SEG_LINETO:
-                    store(tmp, coords, 1, isFirstPoint);
-                    break;
-                case PathIterator.SEG_QUADTO:
-                    store(tmp, coords, 2, isFirstPoint);
-                    break;
-                case PathIterator.SEG_CUBICTO:
-                    store(tmp, coords, 3, isFirstPoint);
-                    break;
-                case PathIterator.SEG_CLOSE:
-                    // No points returned.
-            }
-            isFirstPoint = false;
-            pathIterator.next();
-        }
-        if (isFirstPoint) {
-            // No points found
-            return new float[0];
-        } else {
-            return coords;
-        }
-    }
+        // Get a FlatteningIterator
+        PathIterator iterator = pathDelegate.getJavaShape().getPathIterator(null, error);
 
-    private static void store(float[] src, float[] dst, int count, boolean isFirst) {
-        if (isFirst) {
-            dst[0] = 0;       // fraction
-            dst[1] = src[0];  // abscissa
-            dst[2] = src[1];  // ordinate
+        float segment[] = new float[6];
+        float totalLength = 0;
+        ArrayList<Point2D.Float> points = new ArrayList<Point2D.Float>();
+        Point2D.Float previousPoint = null;
+        while (!iterator.isDone()) {
+            int type = iterator.currentSegment(segment);
+            Point2D.Float currentPoint = new Point2D.Float(segment[0], segment[1]);
+            // MoveTo shouldn't affect the length
+            if (previousPoint != null && type != PathIterator.SEG_MOVETO) {
+                totalLength += currentPoint.distance(previousPoint);
+            }
+            previousPoint = currentPoint;
+            points.add(currentPoint);
+            iterator.next();
         }
-        if (count > 1 || !isFirst) {
-            dst[3] = 1;
-            dst[4] = src[2 * count - 2];
-            dst[5] = src[2 * count - 1];
+
+        int nPoints = points.size();
+        float[] result = new float[nPoints * 3];
+        previousPoint = null;
+        for (int i = 0; i < nPoints; i++) {
+            Point2D.Float point = points.get(i);
+            float distance = previousPoint != null ? (float) previousPoint.distance(point) : .0f;
+            result[i * 3] = distance / totalLength;
+            result[i * 3 + 1] = point.x;
+            result[i * 3 + 2] = point.y;
+
+            totalLength += distance;
+            previousPoint = point;
         }
+
+        return result;
     }
 
     // ---- Private helper methods ----
@@ -735,6 +723,9 @@
      */
     private void cubicTo(float x1, float y1, float x2, float y2,
                         float x3, float y3) {
+        if (isEmpty()) {
+            mPath.moveTo(0, 0);
+        }
         mPath.curveTo(x1, y1, x2, y2, mLastX = x3, mLastY = y3);
     }
 
diff --git a/tools/layoutlib/bridge/src/android/graphics/drawable/GradientDrawable_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/drawable/GradientDrawable_Delegate.java
new file mode 100644
index 0000000..a3ad2aac
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/drawable/GradientDrawable_Delegate.java
@@ -0,0 +1,73 @@
+/*
+ * 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.graphics.drawable;
+
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import android.graphics.Path;
+import android.graphics.drawable.GradientDrawable.GradientState;
+
+import java.lang.reflect.Field;
+
+/**
+ * Delegate implementing the native methods of {@link GradientDrawable}
+ *
+ * Through the layoutlib_create tool, the original native methods of GradientDrawable have been
+ * replaced by calls to methods of the same name in this delegate class.
+ */
+public class GradientDrawable_Delegate {
+
+    /**
+     * The ring can be built either by drawing full circles, or by drawing arcs in case the
+     * circle isn't complete. LayoutLib cannot handle drawing full circles (requires path
+     * subtraction). So, if we need to draw full circles, we switch to drawing 99% circle.
+     */
+    @LayoutlibDelegate
+    /*package*/ static Path buildRing(GradientDrawable thisDrawable, GradientState st) {
+        boolean useLevel = st.mUseLevelForShape;
+        int level = thisDrawable.getLevel();
+        // 10000 is the max level. See android.graphics.drawable.Drawable#getLevel()
+        float sweep = useLevel ? (360.0f * level / 10000.0f) : 360f;
+        Field mLevel = null;
+        if (sweep >= 360 || sweep <= -360) {
+            st.mUseLevelForShape = true;
+            // Use reflection to set the value of the field to prevent setting the drawable to
+            // dirty again.
+            try {
+                mLevel = Drawable.class.getDeclaredField("mLevel");
+                mLevel.setAccessible(true);
+                mLevel.setInt(thisDrawable, 9999);  // set to one less than max.
+            } catch (NoSuchFieldException e) {
+                // The field has been removed in a recent framework change. Fall back to old
+                // buggy behaviour.
+            } catch (IllegalAccessException e) {
+                // We've already set the field to be accessible.
+                assert false;
+            }
+        }
+        Path path = thisDrawable.buildRing_Original(st);
+        st.mUseLevelForShape = useLevel;
+        if (mLevel != null) {
+            try {
+                mLevel.setInt(thisDrawable, level);
+            } catch (IllegalAccessException e) {
+                assert false;
+            }
+        }
+        return path;
+    }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java
index 771c3c8..c2d8d0c 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java
@@ -91,14 +91,6 @@
     }
 
     @Override
-    public void onAnimationStarted(int remainingFrameCount) {
-    }
-
-    @Override
-    public void onAnimationStopped() {
-    }
-
-    @Override
     public void dispatchWindowShown() {
     }
 
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 499bea4..e480ead 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
@@ -152,7 +152,6 @@
      * The list of methods to rewrite as delegates.
      */
     public final static String[] DELEGATE_METHODS = new String[] {
-        "android.animation.AnimatorInflater#loadAnimator",  // TODO: remove when Path.approximate() is supported.
         "android.app.Fragment#instantiate", //(Landroid/content/Context;Ljava/lang/String;Landroid/os/Bundle;)Landroid/app/Fragment;",
         "android.content.res.Resources$Theme#obtainStyledAttributes",
         "android.content.res.Resources$Theme#resolveAttribute",
@@ -162,6 +161,7 @@
         "android.content.res.TypedArray#getValueAt",
         "android.content.res.TypedArray#obtain",
         "android.graphics.BitmapFactory#finishDecode",
+        "android.graphics.drawable.GradientDrawable#buildRing",
         "android.graphics.Typeface#getSystemFontConfigLocation",
         "android.os.Handler#sendMessageAtTime",
         "android.os.HandlerThread#run",
@@ -228,6 +228,7 @@
         "android.graphics.Path",
         "android.graphics.PathDashPathEffect",
         "android.graphics.PathEffect",
+        "android.graphics.PathMeasure",
         "android.graphics.PixelXorXfermode",
         "android.graphics.PorterDuffColorFilter",
         "android.graphics.PorterDuffXfermode",