Merge "Reduce, reuse, recycle SNAPSHOTS!"
diff --git a/api/current.txt b/api/current.txt
index c3a4a0c..09361f2 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5629,7 +5629,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";
@@ -22848,6 +22848,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 {
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 325aa28b..23805a3 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -5750,7 +5750,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 +5792,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);
@@ -24792,6 +24792,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 {
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/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index cb1a89f..42888cf 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;
@@ -2984,11 +2986,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 {
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/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 478fdd1..ae82bd6 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -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;
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..50764f7 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
@@ -2830,134 +2702,26 @@
 
     /**
      * @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.
+     * @deprecated Do not use
+     * @removed
      */
-    public boolean setDeviceInitializer(@Nullable ComponentName admin,
-            @NonNull ComponentName initializer)
-            throws IllegalArgumentException, IllegalStateException {
-        if (mService != null) {
-            try {
-                return mService.setDeviceInitializer(admin, initializer);
-            } catch (RemoteException re) {
-                Log.w(TAG, "Failed to set device initializer");
-            }
-        }
-        return false;
-    }
-
-    /**
-     * @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.
-     */
-    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..74fb11d 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -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/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/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/util/MathUtils.java b/core/java/android/util/MathUtils.java
index acca3ed..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);
     }
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/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 7cf23e7..72bbf40 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -238,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;
@@ -1978,8 +1974,6 @@
             }
         }
 
-        boolean skipDraw = false;
-
         if (mFirst) {
             // handle first focus request
             if (DEBUG_INPUT_RESIZE) Log.v(TAG, "First: mView.hasFocus()="
@@ -1994,11 +1988,6 @@
                             + mView.findFocus());
                 }
             }
-        } else if (mWindowsAnimating) {
-            if (mRemainingFrameCount <= 0) {
-                skipDraw = true;
-            }
-            mRemainingFrameCount--;
         }
 
         final boolean changedVisibility = (viewVisibilityChanged || mFirst) && isViewVisible;
@@ -2043,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
@@ -2787,16 +2774,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;
@@ -3160,8 +3137,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
@@ -3205,10 +3180,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:
@@ -3429,13 +3400,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);
@@ -4048,9 +4012,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);
@@ -4077,12 +4038,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;
@@ -5297,22 +5252,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();
     }
@@ -6224,15 +6163,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.
@@ -6802,22 +6732,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) {
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/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/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index c9b8119..23c4047 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -488,7 +488,7 @@
     }
 
     public void clearContentView() {
-        if (mNonClientDecorView.getChildCount() > 1) {
+        if (mNonClientDecorView != null && mNonClientDecorView.getChildCount() > 1) {
             mNonClientDecorView.removeViewAt(1);
         }
     }
@@ -5413,16 +5413,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/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/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/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/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 9f24e37..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();
     }
 }
 
@@ -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);
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/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/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index ddfd621..38f6e53 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -339,7 +339,7 @@
             // Remember the intersection of the target bounds and the intersection bounds against
             // which we have to crop the content.
             backdropBounds.set(x, y, x + backdropBounds.getWidth(), y + backdropBounds.getHeight());
-            backdropBounds.intersect(targetBounds);
+            backdropBounds.doIntersect(targetBounds);
             // Check if we have to draw something on the left side ...
             if (targetBounds.left < contentBounds.left) {
                 mCanvas->save(SkCanvas::kClip_SaveFlag);
diff --git a/packages/DocumentsUI/AndroidManifest.xml b/packages/DocumentsUI/AndroidManifest.xml
index 295cb80..73744458 100644
--- a/packages/DocumentsUI/AndroidManifest.xml
+++ b/packages/DocumentsUI/AndroidManifest.xml
@@ -3,7 +3,6 @@
 
     <uses-permission android:name="android.permission.MANAGE_DOCUMENTS" />
     <uses-permission android:name="android.permission.REMOVE_TASKS" />
-    <uses-permission android:name="android.permission.REORDER_TASKS" />
 
     <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/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/src/com/android/documentsui/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
index 0abbf4e..a24c936 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
@@ -873,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);
         }
     }
 
@@ -1753,7 +1757,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/FilesActivity.java b/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
index 70ddf59..1330b3c 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
@@ -89,11 +89,13 @@
         RootsFragment.show(getFragmentManager(), null);
 
         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.
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/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/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/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/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index a78351a..b1cc27a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -116,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();
@@ -127,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
@@ -141,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 5c61c5ca6..e647c1f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -26,6 +26,7 @@
 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;
@@ -41,6 +42,8 @@
 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;
@@ -108,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"));
             }
         }
     }
@@ -141,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
                 }
@@ -167,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();
@@ -283,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();
         }
@@ -314,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;
@@ -418,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);
@@ -435,6 +429,7 @@
         // 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;
@@ -557,7 +552,7 @@
     @Override
     public void onTaskLaunchFailed() {
         // Return to Home
-        dismissRecentsToHomeRaw(true);
+        dismissRecentsToHome(true);
     }
 
     @Override
@@ -612,6 +607,18 @@
         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();
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/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 e0f820d..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;
@@ -297,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();
         }
@@ -327,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;
@@ -707,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/TaskStack.java b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
index 8eec87e..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,8 +16,11 @@
 
 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;
@@ -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 de8730d..e406155 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -22,6 +22,7 @@
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Rect;
+import android.graphics.drawable.ColorDrawable;
 import android.os.Bundle;
 import android.os.IRemoteCallback;
 import android.os.RemoteException;
@@ -30,19 +31,25 @@
 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.RecentsTaskLoader;
 import com.android.systemui.recents.model.Task;
@@ -78,6 +85,11 @@
     TaskStackView mTaskStackView;
     RecentsAppWidgetHostView mSearchBar;
     RecentsViewCallbacks mCb;
+
+    RecentsViewTouchHandler mTouchHandler;
+    DragView mDragView;
+    ColorDrawable mDockRegionOverlay;
+
     Interpolator mFastOutSlowInInterpolator;
 
     Rect mSystemInsets = new Rect();
@@ -96,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 */
@@ -200,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++) {
@@ -267,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.
      */
@@ -293,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);
     }
 
@@ -314,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
@@ -323,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
@@ -697,4 +757,64 @@
                     .start();
         }
     }
+
+    /**** EventBus Events ****/
+
+    public final void onBusEvent(DragStartEvent event) {
+        // Add the drag view
+        mDragView = event.dragView;
+        addView(mDragView);
+    }
+
+    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();
+    }
+
+    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/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index 67e3f82..78b9862 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -1296,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) {
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 910db87..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,13 +21,17 @@
 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.animation.AccelerateInterpolator;
@@ -36,17 +40,22 @@
 import android.widget.FrameLayout;
 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;
 
 /* A task view */
 public class TaskView extends FrameLayout implements Task.TaskCallbacks,
-        View.OnClickListener {
+        View.OnClickListener, View.OnLongClickListener {
 
     /** The TaskView callbacks */
     interface TaskViewCallbacks {
@@ -79,6 +88,8 @@
     View mActionButtonView;
     TaskViewCallbacks mCb;
 
+    Point mDownTouchPos = new Point();
+
     Interpolator mFastOutSlowInInterpolator;
     Interpolator mFastOutLinearInInterpolator;
     Interpolator mQuintOutInterpolator;
@@ -169,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);
@@ -608,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. */
@@ -719,6 +742,7 @@
             mHeaderView.rebindToTask(mTask);
             // Rebind any listeners
             mActionButtonView.setOnClickListener(this);
+            setOnLongClickListener(mConfig.hasDockedTasks ? null : this);
         }
         mTaskDataLoaded = true;
     }
@@ -753,4 +777,69 @@
             mCb.onTaskViewClicked(this, mTask, (v == mActionButtonView));
         }
     }
+
+    /**** View.OnLongClickListener Implementation ****/
+
+    @Override
+    public boolean onLongClick(View v) {
+        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 7de8b7b..949d515 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
@@ -244,11 +244,9 @@
                 mLightDismissDrawable : mDarkDismissDrawable);
         mDismissButton.setContentDescription(String.format(mDismissContentDescription,
                 t.contentDescription));
-        mMoveTaskButton.setVisibility((mConfig.multiWindowEnabled) ? View.VISIBLE : View.INVISIBLE);
-        if (mConfig.multiWindowEnabled) {
-            updateResizeTaskBarIcon(t);
-            mMoveTaskButton.setOnClickListener(this);
-        }
+        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().
@@ -263,10 +261,7 @@
         mTask = null;
         mApplicationIcon.setImageDrawable(null);
         mApplicationIcon.setOnClickListener(null);
-
-        if (mConfig.multiWindowEnabled) {
-            mMoveTaskButton.setOnClickListener(null);
-        }
+        mMoveTaskButton.setOnClickListener(null);
     }
 
     /** Updates the resize task bar button. */
@@ -471,7 +466,7 @@
             // 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 = (TaskView) getParent().getParent();
+            TaskView tv = Utilities.findParent(this, TaskView.class);
             tv.dismissTask();
 
             // Keep track of deletions by the dismiss button
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..86f8f5a 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) {
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/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/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 5a27301..7eb8005 100644
--- a/rs/java/android/renderscript/RenderScript.java
+++ b/rs/java/android/renderscript/RenderScript.java
@@ -1585,15 +1585,20 @@
             mMessageThread.mRun = false;
 
             // Wait for mMessageThread to join.  Try in a loop, in case this thread gets interrupted
-            // during the wait.
-            boolean hasJoined = false;
+            // 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) {
+                } 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/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 617264c..dac317a 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -36,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;
@@ -2735,7 +2736,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);
@@ -2752,7 +2753,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);
@@ -4188,27 +4189,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;
@@ -8550,7 +8563,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();
             }
@@ -8663,7 +8677,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;
@@ -8874,7 +8889,8 @@
      * @return Returns true if the given task was found and removed.
      */
     private boolean removeTaskByIdLocked(int taskId, boolean killProcess) {
-        TaskRecord tr = mStackSupervisor.anyTaskForIdLocked(taskId, false);
+        final TaskRecord tr = mStackSupervisor.anyTaskForIdLocked(
+                taskId, !RESTORE_FROM_RECENTS, INVALID_STACK_ID);
         if (tr != null) {
             tr.removeTaskActivitiesLocked();
             cleanUpRemovedTaskLocked(tr, killProcess);
@@ -9203,7 +9219,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 {
@@ -11336,7 +11353,7 @@
     public boolean isTopActivityImmersive() {
         enforceNotIsolatedCaller("startActivity");
         synchronized (this) {
-            ActivityRecord r = getFocusedStack().topRunningActivityLocked(null);
+            ActivityRecord r = getFocusedStack().topRunningActivityLocked();
             return (r != null) ? r.immersive : false;
         }
     }
@@ -17727,7 +17744,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) {
@@ -21071,7 +21088,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 d4e798f..4671cb0 100755
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -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 f549af8..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
@@ -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 ac7b9b1..cda9a5d 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);
@@ -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);
@@ -2979,7 +2987,7 @@
 
         Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizeStack_" + stackId);
 
-        ActivityRecord r = stack.topRunningActivityLocked(null);
+        ActivityRecord r = stack.topRunningActivityLocked();
 
         mTmpBounds.clear();
         mTmpConfigs.clear();
@@ -3104,7 +3112,7 @@
         // 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;
                 kept = stack.ensureActivityConfigurationLocked(r, 0, preserveWindow);
@@ -3147,7 +3155,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) {
@@ -3246,7 +3254,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 */);
         }
@@ -3735,7 +3743,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/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/RotationHelper.java b/services/core/java/com/android/server/audio/RotationHelper.java
index f03e6c7..359cc36 100644
--- a/services/core/java/com/android/server/audio/RotationHelper.java
+++ b/services/core/java/com/android/server/audio/RotationHelper.java
@@ -192,16 +192,18 @@
         }
 
         public void run() {
-            int newRotation;
             while (mWaitCounter < WAIT_TIMES_MS.length) {
-                updateOrientation();
                 int waitTimeMs;
                 synchronized(mCounterLock) {
-                    waitTimeMs = WAIT_TIMES_MS[mWaitCounter];
+                    waitTimeMs = mWaitCounter < WAIT_TIMES_MS.length ?
+                            WAIT_TIMES_MS[mWaitCounter] : 0;
                     mWaitCounter++;
                 }
                 try {
-                    sleep(waitTimeMs);
+                    if (waitTimeMs > 0) {
+                        sleep(waitTimeMs);
+                        updateOrientation();
+                    }
                 } catch (InterruptedException e) { }
             }
         }
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 31fc24b..341410d 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -59,8 +59,6 @@
 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;
@@ -82,7 +80,6 @@
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.List;
-import java.util.Set;
 
 import libcore.io.IoUtils;
 
@@ -147,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
 
@@ -596,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);
@@ -622,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));
@@ -815,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;
                             }
@@ -978,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);
 
@@ -1016,7 +1010,8 @@
             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);
             final int userSize = mUsers.size();
             for (int i = 0; i < userSize; i++) {
@@ -1036,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;
@@ -1143,7 +1099,7 @@
                             name = parser.getText();
                         }
                     } else if (TAG_RESTRICTIONS.equals(tag)) {
-                        readRestrictionsLocked(parser, restrictions);
+                        UserRestrictionsUtils.readRestrictions(parser, restrictions);
                     }
                 }
             }
@@ -1172,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;
@@ -2142,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/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
index c56af39..8870dd1 100644
--- a/services/core/java/com/android/server/wm/DimBehindController.java
+++ b/services/core/java/com/android/server/wm/DimBehindController.java
@@ -159,24 +159,32 @@
 
     boolean animateDimLayers() {
         int fullScreen = -1;
+        int fullScreenAndDimming = -1;
+        boolean result = false;
+
         for (int i = mState.size() - 1; i >= 0; i--) {
-            DimLayer.DimLayerUser dimLayerUser = mState.keyAt(i);
-            if (dimLayerUser.isFullscreen()) {
+            DimLayer.DimLayerUser user = mState.keyAt(i);
+            if (user.isFullscreen()) {
                 fullScreen = i;
                 if (mState.valueAt(i).continueDimming) {
-                    break;
+                    fullScreenAndDimming = i;
                 }
+            } else {
+                // We always want to animate the non fullscreen windows, they don't share their
+                // dim layers.
+                result |= animateDimLayers(user);
             }
         }
-        if (fullScreen != -1) {
-            return animateDimLayers(mState.keyAt(fullScreen));
-        } else {
-            boolean result = false;
-            for (int i = mState.size() - 1; i >= 0; i--) {
-                result |= animateDimLayers(mState.keyAt(i));
-            }
-            return result;
+        // 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) {
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index 8c5d319..b9028e3 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;
@@ -25,16 +26,15 @@
 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.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 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.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.View;
@@ -58,7 +58,6 @@
     private Rect mOriginalRect = new Rect();
     private int mDockSide;
 
-
     DockedStackDividerController(Context context, DisplayContent displayContent) {
         mContext = context;
         mDisplayContent = displayContent;
@@ -66,13 +65,16 @@
                 com.android.internal.R.dimen.docked_stack_divider_thickness);
     }
 
-    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 +94,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();
         }
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index ab1bf20..5138269 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);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index c4201d9..da4af92 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;
@@ -223,7 +226,6 @@
     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;
 
@@ -1005,7 +1007,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++) {
@@ -1039,55 +1041,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
@@ -1189,19 +1155,63 @@
         // 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.
+            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();
 
@@ -3180,9 +3190,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
@@ -3204,8 +3214,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;
                 }
 
@@ -3215,7 +3224,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.
@@ -3226,7 +3235,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
@@ -3239,8 +3248,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.
@@ -3257,19 +3279,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;
@@ -3278,8 +3297,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;
                 }
 
@@ -3293,23 +3312,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;
@@ -3412,7 +3430,6 @@
         }
     }
 
-    @Override
     public void setNewConfiguration(Configuration config) {
         if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
                 "setNewConfiguration()")) {
@@ -3420,12 +3437,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();
+                }
+            }
         }
     }
 
@@ -7165,6 +7190,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) {
@@ -7706,8 +7746,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;
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index ed53501..722ef9f 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -290,6 +290,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.
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index db9fcf1..112646a 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -965,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..cb5ab1b 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);
+        }
     }
 
     /**
@@ -1381,17 +1446,13 @@
         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 +1859,6 @@
         validatePasswordOwnerLocked(policy);
         syncDeviceCapabilitiesLocked(policy);
         updateMaximumTimeToLockLocked(policy);
-        addDeviceInitializerToLockTaskPackagesLocked(userHandle);
         updateLockTaskPackagesLocked(policy.mLockTaskPackages, userHandle);
         if (policy.mStatusBarDisabled) {
             setStatusBarDisabledInternal(policy.mStatusBarDisabled, userHandle);
@@ -3076,7 +3136,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 +3223,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 +3442,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 +3543,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.getPackageName())) {
                         throw new SecurityException(
                                "Only device owner admins can set WIPE_RESET_PROTECTION_DATA");
                     }
@@ -3846,16 +3902,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 +4134,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 +4150,7 @@
             return false;
         }
         synchronized (this) {
-            ActiveAdmin deviceOwner = getDeviceOwnerAdmin();
+            ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
             return (deviceOwner != null) ? deviceOwner.requireAutoTime : false;
         }
     }
@@ -4317,7 +4371,8 @@
     }
 
     // Returns the active device owner or null if there is no device owner.
-    private ActiveAdmin getDeviceOwnerAdmin() {
+    @VisibleForTesting
+    ActiveAdmin getDeviceOwnerAdminLocked() {
         String deviceOwnerPackageName = getDeviceOwner();
         if (deviceOwnerPackageName == null) {
             return null;
@@ -4367,126 +4422,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 +4511,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 +4565,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,7 +5438,7 @@
     @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 =
@@ -5571,37 +5463,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 +5797,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 +5890,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;
         }
     }
@@ -6098,7 +5993,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 +6020,7 @@
 
             long id = mInjector.binderClearCallingIdentity();
             try {
-                Settings.Global.putString(contentResolver, setting, value);
+                mInjector.settingsGlobalPutString(setting, value);
             } finally {
                 mInjector.binderRestoreCallingIdentity(id);
             }
@@ -6155,7 +6049,7 @@
 
             long id = mInjector.binderClearCallingIdentity();
             try {
-                Settings.Secure.putStringForUser(contentResolver, setting, value, callingUserId);
+                mInjector.settingsSecurePutStringForUser(setting, value, callingUserId);
             } finally {
                 mInjector.binderRestoreCallingIdentity(id);
             }
@@ -6278,18 +6172,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 +6188,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 +6312,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 (isDeviceOwner(pkg)) {
                 return true;
             }
         }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
index 370cf48..f9543c2 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;
@@ -90,9 +85,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<>();
 
@@ -167,26 +159,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 +224,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 +405,7 @@
 
         @Override
         boolean shouldWrite() {
-            return (mDeviceOwner != null) || (mDeviceInitializer != null)
-                    || (mSystemUpdatePolicy != null);
+            return (mDeviceOwner != null) || (mSystemUpdatePolicy != null);
         }
 
         @Override
@@ -457,9 +417,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 +445,7 @@
                     break;
                 }
                 case TAG_DEVICE_INITIALIZER:
-                    mDeviceInitializer = OwnerInfo.readFromXml(parser);
+                    // Deprecated tag
                     break;
                 case TAG_SYSTEM_UPDATE_POLICY:
                     mSystemUpdatePolicy = SystemUpdatePolicy.restoreFromXml(parser);
@@ -607,11 +564,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/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
index f4ffe2e..b109e7b 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
@@ -225,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 5b23798..03b892e 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -31,8 +31,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;
@@ -65,8 +65,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;
@@ -207,9 +205,7 @@
         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!
@@ -625,4 +621,97 @@
         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);
+        setUpApplicationInfo(PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED,
+                DpmMockContext.CALLER_SYSTEM_USER_UID);
+
+        // Call.
+        dpm.setActiveAdmin(admin1, /* replace =*/ false, UserHandle.USER_SYSTEM);
+        assertTrue(dpm.setDeviceOwner(admin1.getPackageName(), "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/DpmMockContext.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
index 7b36e88..73d63ea 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
@@ -158,6 +158,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;
 
     /**
@@ -184,6 +211,7 @@
     public final IBackupManager ibackupManager;
     public final IAudioService iaudioService;
     public final LockPatternUtils lockPatternUtils;
+    public final SettingsForMock settings;
 
     /** Note this is a partial mock, not a real mock. */
     public final PackageManager packageManager;
@@ -212,6 +240,7 @@
         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());
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 4a39614..a210d46 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;
@@ -94,7 +97,6 @@
 
             assertFalse(owners.hasDeviceOwner());
             assertEquals(UserHandle.USER_NULL, owners.getDeviceOwnerUserId());
-            assertFalse(owners.hasDeviceInitializer());
             assertNull(owners.getSystemUpdatePolicy());
             assertEquals(0, owners.getProfileOwnerKeys().size());
         }
@@ -106,7 +108,6 @@
 
             assertFalse(owners.hasDeviceOwner());
             assertEquals(UserHandle.USER_NULL, owners.getDeviceOwnerUserId());
-            assertFalse(owners.hasDeviceInitializer());
             assertNull(owners.getSystemUpdatePolicy());
             assertEquals(0, owners.getProfileOwnerKeys().size());
         }
@@ -139,7 +140,6 @@
             assertEquals("com.google.android.testdpc", owners.getDeviceOwnerPackageName());
             assertEquals(UserHandle.USER_SYSTEM, owners.getDeviceOwnerUserId());
 
-            assertFalse(owners.hasDeviceInitializer());
             assertNull(owners.getSystemUpdatePolicy());
             assertEquals(0, owners.getProfileOwnerKeys().size());
         }
@@ -154,7 +154,6 @@
             assertEquals("com.google.android.testdpc", owners.getDeviceOwnerPackageName());
             assertEquals(UserHandle.USER_SYSTEM, owners.getDeviceOwnerUserId());
 
-            assertFalse(owners.hasDeviceInitializer());
             assertNull(owners.getSystemUpdatePolicy());
             assertEquals(0, owners.getProfileOwnerKeys().size());
         }
@@ -184,7 +183,6 @@
 
             assertFalse(owners.hasDeviceOwner());
             assertEquals(UserHandle.USER_NULL, owners.getDeviceOwnerUserId());
-            assertFalse(owners.hasDeviceInitializer());
             assertNull(owners.getSystemUpdatePolicy());
 
             assertEquals(2, owners.getProfileOwnerKeys().size());
@@ -207,7 +205,6 @@
 
             assertFalse(owners.hasDeviceOwner());
             assertEquals(UserHandle.USER_NULL, owners.getDeviceOwnerUserId());
-            assertFalse(owners.hasDeviceInitializer());
             assertNull(owners.getSystemUpdatePolicy());
 
             assertEquals(2, owners.getProfileOwnerKeys().size());
@@ -251,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());
 
@@ -279,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());
 
@@ -322,8 +315,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());
@@ -337,8 +328,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());
@@ -368,7 +357,6 @@
 
             assertFalse(owners.hasDeviceOwner());
             assertEquals(UserHandle.USER_NULL, owners.getDeviceOwnerUserId());
-            assertFalse(owners.hasDeviceInitializer());
             assertEquals(0, owners.getProfileOwnerKeys().size());
 
             assertNotNull(owners.getSystemUpdatePolicy());
@@ -382,7 +370,6 @@
 
             assertFalse(owners.hasDeviceOwner());
             assertEquals(UserHandle.USER_NULL, owners.getDeviceOwnerUserId());
-            assertFalse(owners.hasDeviceInitializer());
             assertEquals(0, owners.getProfileOwnerKeys().size());
 
             assertNotNull(owners.getSystemUpdatePolicy());
@@ -408,7 +395,6 @@
         assertTrue(owners.getProfileOwnerFileWithTestOverride(11).exists());
 
         // Then clear all information and save.
-        owners.clearDeviceInitializer();
         owners.clearDeviceOwner();
         owners.clearSystemUpdatePolicy();
         owners.removeProfileOwner(10);
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/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/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/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() {
     }