Merge "use running sum for ValueMetricProducer bucket simplify ValueMetricProducer logic for pulled data"
diff --git a/api/current.txt b/api/current.txt
index e97670a..a01de4f 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -49586,6 +49586,44 @@
     method public void proceed();
   }
 
+  public class TracingConfig {
+    ctor public TracingConfig(int);
+    ctor public TracingConfig(int, java.lang.String, int);
+    method public java.lang.String getCustomCategoryPattern();
+    method public int getPresetCategories();
+    method public int getTracingMode();
+    field public static final int CATEGORIES_FRAME_VIEWER = 4; // 0x4
+    field public static final int CATEGORIES_INPUT_LATENCY = 1; // 0x1
+    field public static final int CATEGORIES_JAVASCRIPT_AND_RENDERING = 3; // 0x3
+    field public static final int CATEGORIES_NONE = -1; // 0xffffffff
+    field public static final int CATEGORIES_RENDERING = 2; // 0x2
+    field public static final int CATEGORIES_WEB_DEVELOPER = 0; // 0x0
+    field public static final int RECORD_CONTINUOUSLY = 1; // 0x1
+    field public static final int RECORD_TO_CONSOLE = 3; // 0x3
+    field public static final int RECORD_UNTIL_FULL = 0; // 0x0
+    field public static final int RECORD_UNTIL_FULL_LARGE_BUFFER = 2; // 0x2
+  }
+
+  public abstract class TracingController {
+    ctor public TracingController();
+    method public static android.webkit.TracingController getInstance();
+    method public abstract boolean isTracing();
+    method public abstract boolean start(android.webkit.TracingConfig);
+    method public abstract boolean stop();
+    method public abstract boolean stopAndFlush(android.webkit.TracingController.TracingOutputStream, android.os.Handler);
+  }
+
+  public static abstract interface TracingController.TracingOutputStream {
+    method public abstract void complete();
+    method public abstract void write(byte[]);
+  }
+
+  public class TracingFileOutputStream implements android.webkit.TracingController.TracingOutputStream {
+    ctor public TracingFileOutputStream(java.lang.String) throws java.io.FileNotFoundException;
+    method public void complete();
+    method public void write(byte[]);
+  }
+
   public final class URLUtil {
     ctor public URLUtil();
     method public static java.lang.String composeSearchUrl(java.lang.String, java.lang.String, java.lang.String);
diff --git a/api/system-current.txt b/api/system-current.txt
index eb0b133..30e4cbc 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -4637,6 +4637,7 @@
     method public abstract android.webkit.ServiceWorkerController getServiceWorkerController();
     method public abstract android.webkit.WebViewFactoryProvider.Statics getStatics();
     method public abstract android.webkit.TokenBindingService getTokenBindingService();
+    method public abstract android.webkit.TracingController getTracingController();
     method public abstract android.webkit.WebIconDatabase getWebIconDatabase();
     method public abstract android.webkit.WebStorage getWebStorage();
     method public abstract android.webkit.WebViewDatabase getWebViewDatabase(android.content.Context);
diff --git a/cmds/input/src/com/android/commands/input/Input.java b/cmds/input/src/com/android/commands/input/Input.java
index 9ee11f8..d3ec320 100644
--- a/cmds/input/src/com/android/commands/input/Input.java
+++ b/cmds/input/src/com/android/commands/input/Input.java
@@ -88,8 +88,8 @@
                     final boolean longpress = "--longpress".equals(args[index + 1]);
                     final int start = longpress ? index + 2 : index + 1;
                     inputSource = getSource(inputSource, InputDevice.SOURCE_KEYBOARD);
-                    if (length > start) {
-                        for (int i = start; i < length; i++) {
+                    if (args.length > start) {
+                        for (int i = start; i < args.length; i++) {
                             int keyCode = KeyEvent.keyCodeFromString(args[i]);
                             if (keyCode == KeyEvent.KEYCODE_UNKNOWN) {
                                 keyCode = KeyEvent.keyCodeFromString("KEYCODE_" + args[i]);
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 8824643..97dcb90 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -391,8 +391,12 @@
      * @hide
      */
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef({SHOW_MODE_AUTO, SHOW_MODE_HIDDEN})
-    public @interface SoftKeyboardShowMode {};
+    @IntDef(prefix = { "SHOW_MODE_" }, value = {
+            SHOW_MODE_AUTO,
+            SHOW_MODE_HIDDEN
+    })
+    public @interface SoftKeyboardShowMode {}
+
     public static final int SHOW_MODE_AUTO = 0;
     public static final int SHOW_MODE_HIDDEN = 1;
 
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java
index bd9c9fa..782733f 100644
--- a/core/java/android/accounts/AccountManager.java
+++ b/core/java/android/accounts/AccountManager.java
@@ -290,8 +290,13 @@
 
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef({VISIBILITY_UNDEFINED, VISIBILITY_VISIBLE, VISIBILITY_USER_MANAGED_VISIBLE,
-            VISIBILITY_NOT_VISIBLE, VISIBILITY_USER_MANAGED_NOT_VISIBLE})
+    @IntDef(prefix = { "VISIBILITY_" }, value = {
+            VISIBILITY_UNDEFINED,
+            VISIBILITY_VISIBLE,
+            VISIBILITY_USER_MANAGED_VISIBLE,
+            VISIBILITY_NOT_VISIBLE,
+            VISIBILITY_USER_MANAGED_NOT_VISIBLE
+    })
     public @interface AccountVisibility {
     }
 
diff --git a/core/java/android/app/ActionBar.java b/core/java/android/app/ActionBar.java
index 0e8326d..04ff48c 100644
--- a/core/java/android/app/ActionBar.java
+++ b/core/java/android/app/ActionBar.java
@@ -95,7 +95,11 @@
 public abstract class ActionBar {
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef({NAVIGATION_MODE_STANDARD, NAVIGATION_MODE_LIST, NAVIGATION_MODE_TABS})
+    @IntDef(prefix = { "NAVIGATION_MODE_" }, value = {
+            NAVIGATION_MODE_STANDARD,
+            NAVIGATION_MODE_LIST,
+            NAVIGATION_MODE_TABS
+    })
     public @interface NavigationMode {}
 
     /**
@@ -139,15 +143,14 @@
 
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef(flag = true,
-            value = {
-                    DISPLAY_USE_LOGO,
-                    DISPLAY_SHOW_HOME,
-                    DISPLAY_HOME_AS_UP,
-                    DISPLAY_SHOW_TITLE,
-                    DISPLAY_SHOW_CUSTOM,
-                    DISPLAY_TITLE_MULTIPLE_LINES
-            })
+    @IntDef(flag = true, prefix = { "DISPLAY_" }, value = {
+            DISPLAY_USE_LOGO,
+            DISPLAY_SHOW_HOME,
+            DISPLAY_HOME_AS_UP,
+            DISPLAY_SHOW_TITLE,
+            DISPLAY_SHOW_CUSTOM,
+            DISPLAY_TITLE_MULTIPLE_LINES
+    })
     public @interface DisplayOptions {}
 
     /**
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 3ca23f2..847082a 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -175,7 +175,7 @@
      * @hide
      */
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef({
+    @IntDef(prefix = { "BUGREPORT_OPTION_" }, value = {
             BUGREPORT_OPTION_FULL,
             BUGREPORT_OPTION_INTERACTIVE,
             BUGREPORT_OPTION_REMOTE,
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index c1a5104..c943574 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -258,8 +258,10 @@
     public static final int OP_CHANGE_WIFI_STATE = 71;
     /** @hide Request package deletion through package installer */
     public static final int OP_REQUEST_DELETE_PACKAGES = 72;
+    /** @hide Bind an accessibility service. */
+    public static final int OP_BIND_ACCESSIBILITY_SERVICE = 73;
     /** @hide */
-    public static final int _NUM_OP = 73;
+    public static final int _NUM_OP = 74;
 
     /** Access to coarse location information. */
     public static final String OPSTR_COARSE_LOCATION = "android:coarse_location";
@@ -503,6 +505,7 @@
             OP_RUN_ANY_IN_BACKGROUND,
             OP_CHANGE_WIFI_STATE,
             OP_REQUEST_DELETE_PACKAGES,
+            OP_BIND_ACCESSIBILITY_SERVICE,
     };
 
     /**
@@ -583,6 +586,7 @@
             null, // OP_RUN_ANY_IN_BACKGROUND
             null, // OP_CHANGE_WIFI_STATE
             null, // OP_REQUEST_DELETE_PACKAGES
+            null, // OP_BIND_ACCESSIBILITY_SERVICE
     };
 
     /**
@@ -663,6 +667,7 @@
             "RUN_ANY_IN_BACKGROUND",
             "CHANGE_WIFI_STATE",
             "REQUEST_DELETE_PACKAGES",
+            "BIND_ACCESSIBILITY_SERVICE",
     };
 
     /**
@@ -743,6 +748,7 @@
             null, // no permission for OP_RUN_ANY_IN_BACKGROUND
             Manifest.permission.CHANGE_WIFI_STATE,
             Manifest.permission.REQUEST_DELETE_PACKAGES,
+            Manifest.permission.BIND_ACCESSIBILITY_SERVICE,
     };
 
     /**
@@ -824,6 +830,7 @@
             null, // OP_RUN_ANY_IN_BACKGROUND
             null, // OP_CHANGE_WIFI_STATE
             null, // REQUEST_DELETE_PACKAGES
+            null, // OP_BIND_ACCESSIBILITY_SERVICE
     };
 
     /**
@@ -904,6 +911,7 @@
             false, // OP_RUN_ANY_IN_BACKGROUND
             false, // OP_CHANGE_WIFI_STATE
             false, // OP_REQUEST_DELETE_PACKAGES
+            false, // OP_BIND_ACCESSIBILITY_SERVICE
     };
 
     /**
@@ -983,6 +991,7 @@
             AppOpsManager.MODE_ALLOWED,  // OP_RUN_ANY_IN_BACKGROUND
             AppOpsManager.MODE_ALLOWED,  // OP_CHANGE_WIFI_STATE
             AppOpsManager.MODE_ALLOWED,  // REQUEST_DELETE_PACKAGES
+            AppOpsManager.MODE_ALLOWED,  // OP_BIND_ACCESSIBILITY_SERVICE
     };
 
     /**
@@ -1066,6 +1075,7 @@
             false, // OP_RUN_ANY_IN_BACKGROUND
             false, // OP_CHANGE_WIFI_STATE
             false, // OP_REQUEST_DELETE_PACKAGES
+            false, // OP_BIND_ACCESSIBILITY_SERVICE
     };
 
     /**
diff --git a/core/java/android/app/FragmentTransaction.java b/core/java/android/app/FragmentTransaction.java
index 0f4a7fb..1103649 100644
--- a/core/java/android/app/FragmentTransaction.java
+++ b/core/java/android/app/FragmentTransaction.java
@@ -182,7 +182,12 @@
     public static final int TRANSIT_FRAGMENT_FADE = 3 | TRANSIT_ENTER_MASK;
 
     /** @hide */
-    @IntDef({TRANSIT_NONE, TRANSIT_FRAGMENT_OPEN, TRANSIT_FRAGMENT_CLOSE, TRANSIT_FRAGMENT_FADE})
+    @IntDef(prefix = { "TRANSIT_" }, value = {
+            TRANSIT_NONE,
+            TRANSIT_FRAGMENT_OPEN,
+            TRANSIT_FRAGMENT_CLOSE,
+            TRANSIT_FRAGMENT_FADE
+    })
     @Retention(RetentionPolicy.SOURCE)
     public @interface Transit {}
 
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index fb9efe6..705f9a0 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -577,7 +577,13 @@
     public int flags;
 
     /** @hide */
-    @IntDef({PRIORITY_DEFAULT,PRIORITY_LOW,PRIORITY_MIN,PRIORITY_HIGH,PRIORITY_MAX})
+    @IntDef(prefix = { "PRIORITY_" }, value = {
+            PRIORITY_DEFAULT,
+            PRIORITY_LOW,
+            PRIORITY_MIN,
+            PRIORITY_HIGH,
+            PRIORITY_MAX
+    })
     @Retention(RetentionPolicy.SOURCE)
     public @interface Priority {}
 
diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java
index 23c4166..85a9be3 100644
--- a/core/java/android/app/StatusBarManager.java
+++ b/core/java/android/app/StatusBarManager.java
@@ -80,9 +80,14 @@
     public static final int DISABLE2_MASK = DISABLE2_QUICK_SETTINGS | DISABLE2_SYSTEM_ICONS
             | DISABLE2_NOTIFICATION_SHADE | DISABLE2_GLOBAL_ACTIONS;
 
-    @IntDef(flag = true,
-            value = {DISABLE2_NONE, DISABLE2_MASK, DISABLE2_QUICK_SETTINGS, DISABLE2_SYSTEM_ICONS,
-                    DISABLE2_NOTIFICATION_SHADE, DISABLE2_GLOBAL_ACTIONS})
+    @IntDef(flag = true, prefix = { "DISABLE2_" }, value = {
+            DISABLE2_NONE,
+            DISABLE2_MASK,
+            DISABLE2_QUICK_SETTINGS,
+            DISABLE2_SYSTEM_ICONS,
+            DISABLE2_NOTIFICATION_SHADE,
+            DISABLE2_GLOBAL_ACTIONS
+    })
     @Retention(RetentionPolicy.SOURCE)
     public @interface Disable2Flags {}
 
diff --git a/core/java/android/app/UiModeManager.java b/core/java/android/app/UiModeManager.java
index bc61668..0da5e24 100644
--- a/core/java/android/app/UiModeManager.java
+++ b/core/java/android/app/UiModeManager.java
@@ -98,7 +98,11 @@
     public static String ACTION_EXIT_DESK_MODE = "android.app.action.EXIT_DESK_MODE";
 
     /** @hide */
-    @IntDef({MODE_NIGHT_AUTO, MODE_NIGHT_NO, MODE_NIGHT_YES})
+    @IntDef(prefix = { "MODE_" }, value = {
+            MODE_NIGHT_AUTO,
+            MODE_NIGHT_NO,
+            MODE_NIGHT_YES
+    })
     @Retention(RetentionPolicy.SOURCE)
     public @interface NightMode {}
 
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index 081bd81..3829afb 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -176,7 +176,7 @@
     // flags for which kind of wallpaper to act on
 
     /** @hide */
-    @IntDef(flag = true, value = {
+    @IntDef(flag = true, prefix = { "FLAG_" }, value = {
             FLAG_SYSTEM,
             FLAG_LOCK
     })
diff --git a/core/java/android/app/WindowConfiguration.java b/core/java/android/app/WindowConfiguration.java
index 80399ae..085fc79 100644
--- a/core/java/android/app/WindowConfiguration.java
+++ b/core/java/android/app/WindowConfiguration.java
@@ -89,7 +89,7 @@
     public static final int WINDOWING_MODE_FREEFORM = 5;
 
     /** @hide */
-    @IntDef({
+    @IntDef(prefix = { "WINDOWING_MODE_" }, value = {
             WINDOWING_MODE_UNDEFINED,
             WINDOWING_MODE_FULLSCREEN,
             WINDOWING_MODE_PINNED,
@@ -115,7 +115,7 @@
     public static final int ACTIVITY_TYPE_ASSISTANT = 4;
 
     /** @hide */
-    @IntDef({
+    @IntDef(prefix = { "ACTIVITY_TYPE_" }, value = {
             ACTIVITY_TYPE_UNDEFINED,
             ACTIVITY_TYPE_STANDARD,
             ACTIVITY_TYPE_HOME,
@@ -138,13 +138,12 @@
     public static final int WINDOW_CONFIG_ACTIVITY_TYPE = 1 << 3;
 
     /** @hide */
-    @IntDef(flag = true,
-            value = {
-                    WINDOW_CONFIG_BOUNDS,
-                    WINDOW_CONFIG_APP_BOUNDS,
-                    WINDOW_CONFIG_WINDOWING_MODE,
-                    WINDOW_CONFIG_ACTIVITY_TYPE
-            })
+    @IntDef(flag = true, prefix = { "WINDOW_CONFIG_" }, value = {
+            WINDOW_CONFIG_BOUNDS,
+            WINDOW_CONFIG_APP_BOUNDS,
+            WINDOW_CONFIG_WINDOWING_MODE,
+            WINDOW_CONFIG_ACTIVITY_TYPE
+    })
     public @interface WindowConfig {}
 
     public WindowConfiguration() {
diff --git a/core/java/android/app/admin/DeviceAdminReceiver.java b/core/java/android/app/admin/DeviceAdminReceiver.java
index d0d98c9..2e697ac 100644
--- a/core/java/android/app/admin/DeviceAdminReceiver.java
+++ b/core/java/android/app/admin/DeviceAdminReceiver.java
@@ -368,9 +368,9 @@
      * @hide
      */
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef({
-        BUGREPORT_FAILURE_FAILED_COMPLETING,
-        BUGREPORT_FAILURE_FILE_NO_LONGER_AVAILABLE
+    @IntDef(prefix = { "BUGREPORT_FAILURE_" }, value = {
+            BUGREPORT_FAILURE_FAILED_COMPLETING,
+            BUGREPORT_FAILURE_FILE_NO_LONGER_AVAILABLE
     })
     public @interface BugreportFailureCode {}
 
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index e43ae5b..3c4036b 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -1376,8 +1376,13 @@
     /**
      * @hide
      */
-    @IntDef({STATE_USER_UNMANAGED, STATE_USER_SETUP_INCOMPLETE, STATE_USER_SETUP_COMPLETE,
-            STATE_USER_SETUP_FINALIZED, STATE_USER_PROFILE_COMPLETE})
+    @IntDef(prefix = { "STATE_USER_" }, value = {
+            STATE_USER_UNMANAGED,
+            STATE_USER_SETUP_INCOMPLETE,
+            STATE_USER_SETUP_COMPLETE,
+            STATE_USER_SETUP_FINALIZED,
+            STATE_USER_PROFILE_COMPLETE
+    })
     @Retention(RetentionPolicy.SOURCE)
     public @interface UserProvisioningState {}
 
@@ -1550,11 +1555,13 @@
      * @hide
      */
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef({CODE_OK, CODE_HAS_DEVICE_OWNER, CODE_USER_HAS_PROFILE_OWNER, CODE_USER_NOT_RUNNING,
+    @IntDef(prefix = { "CODE_" }, value = {
+            CODE_OK, CODE_HAS_DEVICE_OWNER, CODE_USER_HAS_PROFILE_OWNER, CODE_USER_NOT_RUNNING,
             CODE_USER_SETUP_COMPLETED, CODE_NOT_SYSTEM_USER, CODE_HAS_PAIRED,
             CODE_MANAGED_USERS_NOT_SUPPORTED, CODE_SYSTEM_USER, CODE_CANNOT_ADD_MANAGED_PROFILE,
             CODE_NOT_SYSTEM_USER_SPLIT, CODE_DEVICE_ADMIN_NOT_SUPPORTED,
-            CODE_SPLIT_SYSTEM_USER_DEVICE_SYSTEM_USER, CODE_ADD_MANAGED_PROFILE_DISALLOWED})
+            CODE_SPLIT_SYSTEM_USER_DEVICE_SYSTEM_USER, CODE_ADD_MANAGED_PROFILE_DISALLOWED
+    })
     public @interface ProvisioningPreCondition {}
 
     /**
@@ -1636,11 +1643,15 @@
      * @hide
      */
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef(flag = true,
-            value = {LOCK_TASK_FEATURE_NONE, LOCK_TASK_FEATURE_SYSTEM_INFO,
-                    LOCK_TASK_FEATURE_NOTIFICATIONS, LOCK_TASK_FEATURE_HOME,
-                    LOCK_TASK_FEATURE_RECENTS, LOCK_TASK_FEATURE_GLOBAL_ACTIONS,
-                    LOCK_TASK_FEATURE_KEYGUARD})
+    @IntDef(flag = true, prefix = { "LOCK_TASK_FEATURE_" }, value = {
+            LOCK_TASK_FEATURE_NONE,
+            LOCK_TASK_FEATURE_SYSTEM_INFO,
+            LOCK_TASK_FEATURE_NOTIFICATIONS,
+            LOCK_TASK_FEATURE_HOME,
+            LOCK_TASK_FEATURE_RECENTS,
+            LOCK_TASK_FEATURE_GLOBAL_ACTIONS,
+            LOCK_TASK_FEATURE_KEYGUARD
+    })
     public @interface LockTaskFeature {}
 
     /**
@@ -3169,7 +3180,9 @@
 
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef(flag=true, value={FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY})
+    @IntDef(flag = true, prefix = { "FLAG_EVICT_" }, value = {
+            FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY
+    })
     public @interface LockNowFlag {}
 
     /**
@@ -6233,12 +6246,13 @@
     /**
      * @hide
      */
-    @IntDef(
-            flag = true,
-            prefix = {"SKIP_", "MAKE_USER_", "START_", "LEAVE_"},
-            value = {SKIP_SETUP_WIZARD, MAKE_USER_EPHEMERAL, MAKE_USER_DEMO,
-                    START_USER_IN_BACKGROUND, LEAVE_ALL_SYSTEM_APPS_ENABLED}
-    )
+    @IntDef(flag = true, prefix = { "SKIP_", "MAKE_USER_", "START_", "LEAVE_" }, value = {
+            SKIP_SETUP_WIZARD,
+            MAKE_USER_EPHEMERAL,
+            MAKE_USER_DEMO,
+            START_USER_IN_BACKGROUND,
+            LEAVE_ALL_SYSTEM_APPS_ENABLED
+    })
     @Retention(RetentionPolicy.SOURCE)
     public @interface CreateAndManageUserFlags {}
 
@@ -8739,4 +8753,44 @@
             throw re.rethrowFromSystemServer();
         }
     }
+
+    //TODO STOPSHIP Add link to onTransferComplete callback when implemented.
+    /**
+     * Transfers the current administrator. All policies from the current administrator are
+     * migrated to the new administrator. The whole operation is atomic - the transfer is either
+     * complete or not done at all.
+     *
+     * Depending on the current administrator (device owner, profile owner, corporate owned
+     * profile owner), you have the following expected behaviour:
+     * <ul>
+     *     <li>A device owner can only be transferred to a new device owner</li>
+     *     <li>A profile owner can only be transferred to a new profile owner</li>
+     *     <li>A corporate owned managed profile can have two cases:
+     *          <ul>
+     *              <li>If the device owner and profile owner are the same package,
+     *              both will be transferred.</li>
+     *              <li>If the device owner and profile owner are different packages,
+     *              and if this method is called from the profile owner, only the profile owner
+     *              is transferred. Similarly, if it is called from the device owner, only
+     *              the device owner is transferred.</li>
+     *          </ul>
+     *     </li>
+     * </ul>
+     *
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+     * @param target Which {@link DeviceAdminReceiver} we want the new administrator to be.
+     * @param bundle Parameters - This bundle allows the current administrator to pass data to the
+     *               new administrator. The parameters will be received in the
+     *               onTransferComplete callback.
+     * @hide
+     */
+    public void transferOwner(@NonNull ComponentName admin, @NonNull ComponentName target,
+            PersistableBundle bundle) {
+        throwIfParentInstance("transferOwner");
+        try {
+            mService.transferOwner(admin, target, bundle);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
 }
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index b76618b..014d7b9 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -378,4 +378,5 @@
     boolean isLogoutEnabled();
 
     List<String> getDisallowedSystemApps(in ComponentName admin, int userId, String provisioningAction);
+    void transferOwner(in ComponentName admin, in ComponentName target, in PersistableBundle bundle);
 }
diff --git a/core/java/android/app/admin/PasswordMetrics.java b/core/java/android/app/admin/PasswordMetrics.java
index 4658a47..5fee853 100644
--- a/core/java/android/app/admin/PasswordMetrics.java
+++ b/core/java/android/app/admin/PasswordMetrics.java
@@ -223,7 +223,12 @@
     }
 
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef({CHAR_UPPER_CASE, CHAR_LOWER_CASE, CHAR_DIGIT, CHAR_SYMBOL})
+    @IntDef(prefix = { "CHAR_" }, value = {
+            CHAR_UPPER_CASE,
+            CHAR_LOWER_CASE,
+            CHAR_DIGIT,
+            CHAR_SYMBOL
+    })
     private @interface CharacterCatagory {}
     private static final int CHAR_LOWER_CASE = 0;
     private static final int CHAR_UPPER_CASE = 1;
diff --git a/core/java/android/app/admin/SecurityLog.java b/core/java/android/app/admin/SecurityLog.java
index 2b590e0..0f93c59 100644
--- a/core/java/android/app/admin/SecurityLog.java
+++ b/core/java/android/app/admin/SecurityLog.java
@@ -43,10 +43,17 @@
 
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef({TAG_ADB_SHELL_INTERACTIVE, TAG_ADB_SHELL_CMD, TAG_SYNC_RECV_FILE, TAG_SYNC_SEND_FILE,
-        TAG_APP_PROCESS_START, TAG_KEYGUARD_DISMISSED, TAG_KEYGUARD_DISMISS_AUTH_ATTEMPT,
-        TAG_KEYGUARD_SECURED})
-    public @interface SECURITY_LOG_TAG {}
+    @IntDef(prefix = { "TAG_" }, value = {
+            TAG_ADB_SHELL_INTERACTIVE,
+            TAG_ADB_SHELL_CMD,
+            TAG_SYNC_RECV_FILE,
+            TAG_SYNC_SEND_FILE,
+            TAG_APP_PROCESS_START,
+            TAG_KEYGUARD_DISMISSED,
+            TAG_KEYGUARD_DISMISS_AUTH_ATTEMPT,
+            TAG_KEYGUARD_SECURED
+    })
+    public @interface SecurityLogTag {}
 
     /**
      * Indicate that an ADB interactive shell was opened via "adb shell".
@@ -143,13 +150,8 @@
 
         /**
          * Returns the tag of this log entry, which specifies entry's semantics.
-         * Could be one of {@link SecurityLog#TAG_SYNC_RECV_FILE},
-         * {@link SecurityLog#TAG_SYNC_SEND_FILE}, {@link SecurityLog#TAG_ADB_SHELL_CMD},
-         * {@link SecurityLog#TAG_ADB_SHELL_INTERACTIVE}, {@link SecurityLog#TAG_APP_PROCESS_START},
-         * {@link SecurityLog#TAG_KEYGUARD_DISMISSED}, {@link SecurityLog#TAG_KEYGUARD_SECURED},
-         * {@link SecurityLog#TAG_KEYGUARD_DISMISS_AUTH_ATTEMPT}.
          */
-        public @SECURITY_LOG_TAG int getTag() {
+        public @SecurityLogTag int getTag() {
             return mEvent.getTag();
         }
 
diff --git a/core/java/android/app/admin/SystemUpdateInfo.java b/core/java/android/app/admin/SystemUpdateInfo.java
index fa31273..b0376b5 100644
--- a/core/java/android/app/admin/SystemUpdateInfo.java
+++ b/core/java/android/app/admin/SystemUpdateInfo.java
@@ -52,7 +52,11 @@
 
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef({SECURITY_PATCH_STATE_FALSE, SECURITY_PATCH_STATE_TRUE, SECURITY_PATCH_STATE_UNKNOWN})
+    @IntDef(prefix = { "SECURITY_PATCH_STATE_" }, value = {
+            SECURITY_PATCH_STATE_FALSE,
+            SECURITY_PATCH_STATE_TRUE,
+            SECURITY_PATCH_STATE_UNKNOWN
+    })
     public @interface SecurityPatchState {}
 
     private static final String ATTR_RECEIVED_TIME = "received-time";
diff --git a/core/java/android/app/admin/SystemUpdatePolicy.java b/core/java/android/app/admin/SystemUpdatePolicy.java
index 995d98a..232a688 100644
--- a/core/java/android/app/admin/SystemUpdatePolicy.java
+++ b/core/java/android/app/admin/SystemUpdatePolicy.java
@@ -36,10 +36,11 @@
 public class SystemUpdatePolicy implements Parcelable {
 
     /** @hide */
-    @IntDef({
-        TYPE_INSTALL_AUTOMATIC,
-        TYPE_INSTALL_WINDOWED,
-        TYPE_POSTPONE})
+    @IntDef(prefix = { "TYPE_" }, value = {
+            TYPE_INSTALL_AUTOMATIC,
+            TYPE_INSTALL_WINDOWED,
+            TYPE_POSTPONE
+    })
     @Retention(RetentionPolicy.SOURCE)
     @interface SystemUpdatePolicyType {}
 
diff --git a/core/java/android/app/servertransaction/ActivityLifecycleItem.java b/core/java/android/app/servertransaction/ActivityLifecycleItem.java
index 24141e5..0fdc7c5 100644
--- a/core/java/android/app/servertransaction/ActivityLifecycleItem.java
+++ b/core/java/android/app/servertransaction/ActivityLifecycleItem.java
@@ -27,8 +27,17 @@
  */
 public abstract class ActivityLifecycleItem extends ClientTransactionItem {
 
-    @IntDef({UNDEFINED, PRE_ON_CREATE, ON_CREATE, ON_START, ON_RESUME, ON_PAUSE, ON_STOP,
-            ON_DESTROY, ON_RESTART})
+    @IntDef(prefix = { "UNDEFINED", "PRE_", "ON_" }, value = {
+            UNDEFINED,
+            PRE_ON_CREATE,
+            ON_CREATE,
+            ON_START,
+            ON_RESUME,
+            ON_PAUSE,
+            ON_STOP,
+            ON_DESTROY,
+            ON_RESTART
+    })
     @Retention(RetentionPolicy.SOURCE)
     public @interface LifecycleState{}
     public static final int UNDEFINED = -1;
diff --git a/core/java/android/app/timezone/Callback.java b/core/java/android/app/timezone/Callback.java
index aea8038..e3840be 100644
--- a/core/java/android/app/timezone/Callback.java
+++ b/core/java/android/app/timezone/Callback.java
@@ -30,9 +30,14 @@
 public abstract class Callback {
 
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef({SUCCESS, ERROR_UNKNOWN_FAILURE, ERROR_INSTALL_BAD_DISTRO_STRUCTURE,
-        ERROR_INSTALL_BAD_DISTRO_FORMAT_VERSION, ERROR_INSTALL_RULES_TOO_OLD,
-        ERROR_INSTALL_VALIDATION_ERROR})
+    @IntDef(prefix = { "SUCCESS", "ERROR_" }, value = {
+            SUCCESS,
+            ERROR_UNKNOWN_FAILURE,
+            ERROR_INSTALL_BAD_DISTRO_STRUCTURE,
+            ERROR_INSTALL_BAD_DISTRO_FORMAT_VERSION,
+            ERROR_INSTALL_RULES_TOO_OLD,
+            ERROR_INSTALL_VALIDATION_ERROR
+    })
     public @interface AsyncResultCode {}
 
     /**
diff --git a/core/java/android/app/timezone/RulesManager.java b/core/java/android/app/timezone/RulesManager.java
index 417e7d2..0a38eb9 100644
--- a/core/java/android/app/timezone/RulesManager.java
+++ b/core/java/android/app/timezone/RulesManager.java
@@ -69,7 +69,11 @@
     private static final boolean DEBUG = false;
 
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef({SUCCESS, ERROR_UNKNOWN_FAILURE, ERROR_OPERATION_IN_PROGRESS})
+    @IntDef(prefix = { "SUCCESS", "ERROR_" }, value = {
+            SUCCESS,
+            ERROR_UNKNOWN_FAILURE,
+            ERROR_OPERATION_IN_PROGRESS
+    })
     public @interface ResultCode {}
 
     /**
diff --git a/core/java/android/app/timezone/RulesState.java b/core/java/android/app/timezone/RulesState.java
index ec247eb..16309fa 100644
--- a/core/java/android/app/timezone/RulesState.java
+++ b/core/java/android/app/timezone/RulesState.java
@@ -63,11 +63,12 @@
 public final class RulesState implements Parcelable {
 
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef({
+    @IntDef(prefix = { "STAGED_OPERATION_" }, value = {
             STAGED_OPERATION_UNKNOWN,
             STAGED_OPERATION_NONE,
             STAGED_OPERATION_UNINSTALL,
-            STAGED_OPERATION_INSTALL })
+            STAGED_OPERATION_INSTALL
+    })
     private @interface StagedOperationType {}
 
     /** Staged state could not be determined. */
@@ -80,10 +81,11 @@
     public static final int STAGED_OPERATION_INSTALL = 3;
 
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef({
+    @IntDef(prefix = { "DISTRO_STATUS_" }, value = {
             DISTRO_STATUS_UNKNOWN,
             DISTRO_STATUS_NONE,
-            DISTRO_STATUS_INSTALLED })
+            DISTRO_STATUS_INSTALLED
+    })
     private @interface DistroStatus {}
 
     /** The current distro status could not be determined. */
diff --git a/core/java/android/app/usage/NetworkStats.java b/core/java/android/app/usage/NetworkStats.java
index 222e9a0..2e44a63 100644
--- a/core/java/android/app/usage/NetworkStats.java
+++ b/core/java/android/app/usage/NetworkStats.java
@@ -129,7 +129,11 @@
      */
     public static class Bucket {
         /** @hide */
-        @IntDef({STATE_ALL, STATE_DEFAULT, STATE_FOREGROUND})
+        @IntDef(prefix = { "STATE_" }, value = {
+                STATE_ALL,
+                STATE_DEFAULT,
+                STATE_FOREGROUND
+        })
         @Retention(RetentionPolicy.SOURCE)
         public @interface State {}
 
@@ -164,7 +168,11 @@
         public static final int UID_TETHERING = TrafficStats.UID_TETHERING;
 
         /** @hide */
-        @IntDef({METERED_ALL, METERED_NO, METERED_YES})
+        @IntDef(prefix = { "METERED_" }, value = {
+                METERED_ALL,
+                METERED_NO,
+                METERED_YES
+        })
         @Retention(RetentionPolicy.SOURCE)
         public @interface Metered {}
 
@@ -187,7 +195,11 @@
         public static final int METERED_YES = 0x2;
 
         /** @hide */
-        @IntDef({ROAMING_ALL, ROAMING_NO, ROAMING_YES})
+        @IntDef(prefix = { "ROAMING_" }, value = {
+                ROAMING_ALL,
+                ROAMING_NO,
+                ROAMING_YES
+        })
         @Retention(RetentionPolicy.SOURCE)
         public @interface Roaming {}
 
diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java
index 8200414..f04e907 100644
--- a/core/java/android/app/usage/UsageEvents.java
+++ b/core/java/android/app/usage/UsageEvents.java
@@ -110,10 +110,9 @@
         public static final int FLAG_IS_PACKAGE_INSTANT_APP = 1 << 0;
 
         /** @hide */
-        @IntDef(flag = true,
-                value = {
-                        FLAG_IS_PACKAGE_INSTANT_APP,
-                })
+        @IntDef(flag = true, prefix = { "FLAG_" }, value = {
+                FLAG_IS_PACKAGE_INSTANT_APP,
+        })
         @Retention(RetentionPolicy.SOURCE)
         public @interface EventFlags {}
 
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index 1fc45c9..edb6a74 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -151,7 +151,7 @@
     public static final String REASON_PREDICTED = "predicted";
 
     /** @hide */
-    @IntDef(flag = false, value = {
+    @IntDef(flag = false, prefix = { "STANDBY_BUCKET_" }, value = {
             STANDBY_BUCKET_EXEMPTED,
             STANDBY_BUCKET_ACTIVE,
             STANDBY_BUCKET_WORKING_SET,
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index c7be0f3..3290d57 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -156,7 +156,7 @@
             "android.bluetooth.adapter.extra.PREVIOUS_STATE";
 
     /** @hide */
-    @IntDef({
+    @IntDef(prefix = { "STATE_" }, value = {
             STATE_OFF,
             STATE_TURNING_ON,
             STATE_ON,
@@ -357,7 +357,11 @@
             "android.bluetooth.adapter.extra.PREVIOUS_SCAN_MODE";
 
     /** @hide */
-    @IntDef({SCAN_MODE_NONE, SCAN_MODE_CONNECTABLE, SCAN_MODE_CONNECTABLE_DISCOVERABLE})
+    @IntDef(prefix = { "SCAN_" }, value = {
+            SCAN_MODE_NONE,
+            SCAN_MODE_CONNECTABLE,
+            SCAN_MODE_CONNECTABLE_DISCOVERABLE
+    })
     @Retention(RetentionPolicy.SOURCE)
     public @interface ScanMode {}
 
diff --git a/core/java/android/companion/DeviceFilter.java b/core/java/android/companion/DeviceFilter.java
index 9b4fdfd..10135a4 100644
--- a/core/java/android/companion/DeviceFilter.java
+++ b/core/java/android/companion/DeviceFilter.java
@@ -62,7 +62,11 @@
     }
 
     /** @hide */
-    @IntDef({MEDIUM_TYPE_BLUETOOTH, MEDIUM_TYPE_BLUETOOTH_LE, MEDIUM_TYPE_WIFI})
+    @IntDef(prefix = { "MEDIUM_TYPE_" }, value = {
+            MEDIUM_TYPE_BLUETOOTH,
+            MEDIUM_TYPE_BLUETOOTH_LE,
+            MEDIUM_TYPE_WIFI
+    })
     @Retention(RetentionPolicy.SOURCE)
     @interface MediumType {}
 }
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 9ccc552..8d2e141 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -335,7 +335,7 @@
     public static final String EXTRA_HONORED_ARGS = "android.content.extra.HONORED_ARGS";
 
     /** @hide */
-    @IntDef(flag = false, value = {
+    @IntDef(flag = false, prefix = { "QUERY_SORT_DIRECTION_" }, value = {
             QUERY_SORT_DIRECTION_ASCENDING,
             QUERY_SORT_DIRECTION_DESCENDING
     })
@@ -482,11 +482,10 @@
     public static final int SYNC_OBSERVER_TYPE_ALL = 0x7fffffff;
 
     /** @hide */
-    @IntDef(flag = true,
-            value = {
-                NOTIFY_SYNC_TO_NETWORK,
-                NOTIFY_SKIP_NOTIFY_FOR_DESCENDANTS
-            })
+    @IntDef(flag = true, prefix = { "NOTIFY_" }, value = {
+            NOTIFY_SYNC_TO_NETWORK,
+            NOTIFY_SKIP_NOTIFY_FOR_DESCENDANTS
+    })
     @Retention(RetentionPolicy.SOURCE)
     public @interface NotifyFlags {}
 
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 458ba05..137c169 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -221,17 +221,16 @@
     public static final int MODE_NO_LOCALIZED_COLLATORS = 0x0010;
 
     /** @hide */
-    @IntDef(flag = true,
-            value = {
-                BIND_AUTO_CREATE,
-                BIND_DEBUG_UNBIND,
-                BIND_NOT_FOREGROUND,
-                BIND_ABOVE_CLIENT,
-                BIND_ALLOW_OOM_MANAGEMENT,
-                BIND_WAIVE_PRIORITY,
-                BIND_IMPORTANT,
-                BIND_ADJUST_WITH_ACTIVITY
-            })
+    @IntDef(flag = true, prefix = { "BIND_" }, value = {
+            BIND_AUTO_CREATE,
+            BIND_DEBUG_UNBIND,
+            BIND_NOT_FOREGROUND,
+            BIND_ABOVE_CLIENT,
+            BIND_ALLOW_OOM_MANAGEMENT,
+            BIND_WAIVE_PRIORITY,
+            BIND_IMPORTANT,
+            BIND_ADJUST_WITH_ACTIVITY
+    })
     @Retention(RetentionPolicy.SOURCE)
     public @interface BindServiceFlags {}
 
@@ -406,10 +405,9 @@
     public static final int BIND_EXTERNAL_SERVICE = 0x80000000;
 
     /** @hide */
-    @IntDef(flag = true,
-            value = {
-                RECEIVER_VISIBLE_TO_INSTANT_APPS
-            })
+    @IntDef(flag = true, prefix = { "RECEIVER_VISIBLE_" }, value = {
+            RECEIVER_VISIBLE_TO_INSTANT_APPS
+    })
     @Retention(RetentionPolicy.SOURCE)
     public @interface RegisterReceiverFlags {}
 
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 6fb1afd..e940769 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -5690,11 +5690,14 @@
     private static final int COPY_MODE_HISTORY = 2;
 
     /** @hide */
-    @IntDef(value = {COPY_MODE_ALL, COPY_MODE_FILTER, COPY_MODE_HISTORY})
+    @IntDef(prefix = { "COPY_MODE_" }, value = {
+            COPY_MODE_ALL,
+            COPY_MODE_FILTER,
+            COPY_MODE_HISTORY
+    })
     @Retention(RetentionPolicy.SOURCE)
     public @interface CopyMode {}
 
-
     /**
      * Create an empty intent.
      */
@@ -8966,17 +8969,16 @@
     }
 
     /** @hide */
-    @IntDef(flag = true,
-            value = {
-                    FILL_IN_ACTION,
-                    FILL_IN_DATA,
-                    FILL_IN_CATEGORIES,
-                    FILL_IN_COMPONENT,
-                    FILL_IN_PACKAGE,
-                    FILL_IN_SOURCE_BOUNDS,
-                    FILL_IN_SELECTOR,
-                    FILL_IN_CLIP_DATA
-            })
+    @IntDef(flag = true, prefix = { "FILL_IN_" }, value = {
+            FILL_IN_ACTION,
+            FILL_IN_DATA,
+            FILL_IN_CATEGORIES,
+            FILL_IN_COMPONENT,
+            FILL_IN_PACKAGE,
+            FILL_IN_SOURCE_BOUNDS,
+            FILL_IN_SELECTOR,
+            FILL_IN_CLIP_DATA
+    })
     @Retention(RetentionPolicy.SOURCE)
     public @interface FillInFlags {}
 
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index f8cdce6..1461711 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -262,10 +262,10 @@
     public static final int COLOR_MODE_HDR = 2;
 
     /** @hide */
-    @IntDef({
-        COLOR_MODE_DEFAULT,
-        COLOR_MODE_WIDE_COLOR_GAMUT,
-        COLOR_MODE_HDR,
+    @IntDef(prefix = { "COLOR_MODE_" }, value = {
+            COLOR_MODE_DEFAULT,
+            COLOR_MODE_WIDE_COLOR_GAMUT,
+            COLOR_MODE_HDR,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface ColorMode {}
@@ -492,7 +492,7 @@
     public int flags;
 
     /** @hide */
-    @IntDef({
+    @IntDef(prefix = { "SCREEN_ORIENTATION_" }, value = {
             SCREEN_ORIENTATION_UNSET,
             SCREEN_ORIENTATION_UNSPECIFIED,
             SCREEN_ORIENTATION_LANDSCAPE,
@@ -638,25 +638,24 @@
     public int screenOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
 
     /** @hide */
-    @IntDef(flag = true,
-            value = {
-                    CONFIG_MCC,
-                    CONFIG_MNC,
-                    CONFIG_LOCALE,
-                    CONFIG_TOUCHSCREEN,
-                    CONFIG_KEYBOARD,
-                    CONFIG_KEYBOARD_HIDDEN,
-                    CONFIG_NAVIGATION,
-                    CONFIG_ORIENTATION,
-                    CONFIG_SCREEN_LAYOUT,
-                    CONFIG_UI_MODE,
-                    CONFIG_SCREEN_SIZE,
-                    CONFIG_SMALLEST_SCREEN_SIZE,
-                    CONFIG_DENSITY,
-                    CONFIG_LAYOUT_DIRECTION,
-                    CONFIG_COLOR_MODE,
-                    CONFIG_FONT_SCALE,
-            })
+    @IntDef(flag = true, prefix = { "CONFIG_" }, value = {
+            CONFIG_MCC,
+            CONFIG_MNC,
+            CONFIG_LOCALE,
+            CONFIG_TOUCHSCREEN,
+            CONFIG_KEYBOARD,
+            CONFIG_KEYBOARD_HIDDEN,
+            CONFIG_NAVIGATION,
+            CONFIG_ORIENTATION,
+            CONFIG_SCREEN_LAYOUT,
+            CONFIG_UI_MODE,
+            CONFIG_SCREEN_SIZE,
+            CONFIG_SMALLEST_SCREEN_SIZE,
+            CONFIG_DENSITY,
+            CONFIG_LAYOUT_DIRECTION,
+            CONFIG_COLOR_MODE,
+            CONFIG_FONT_SCALE,
+    })
     @Retention(RetentionPolicy.SOURCE)
     public @interface Config {}
 
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index d09ba0b..b4a7eec 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -343,14 +343,13 @@
         public static final int FLAG_GET_KEY_FIELDS_ONLY = 1 << 2;
 
         /** @hide */
-        @IntDef(flag = true,
-                value = {
-                        FLAG_MATCH_DYNAMIC,
-                        FLAG_MATCH_PINNED,
-                        FLAG_MATCH_MANIFEST,
-                        FLAG_GET_KEY_FIELDS_ONLY,
-                        FLAG_MATCH_MANIFEST,
-                })
+        @IntDef(flag = true, prefix = { "FLAG_" }, value = {
+                FLAG_MATCH_DYNAMIC,
+                FLAG_MATCH_PINNED,
+                FLAG_MATCH_MANIFEST,
+                FLAG_GET_KEY_FIELDS_ONLY,
+                FLAG_MATCH_MANIFEST,
+        })
         @Retention(RetentionPolicy.SOURCE)
         public @interface QueryFlags {}
 
@@ -1380,7 +1379,10 @@
         public static final int REQUEST_TYPE_APPWIDGET = 2;
 
         /** @hide */
-        @IntDef(value = {REQUEST_TYPE_SHORTCUT})
+        @IntDef(prefix = { "REQUEST_TYPE_" }, value = {
+                REQUEST_TYPE_SHORTCUT,
+                REQUEST_TYPE_APPWIDGET
+        })
         @Retention(RetentionPolicy.SOURCE)
         public @interface RequestType {}
 
diff --git a/core/java/android/content/pm/SharedLibraryInfo.java b/core/java/android/content/pm/SharedLibraryInfo.java
index 2f1b256..33bc951 100644
--- a/core/java/android/content/pm/SharedLibraryInfo.java
+++ b/core/java/android/content/pm/SharedLibraryInfo.java
@@ -36,13 +36,11 @@
 public final class SharedLibraryInfo implements Parcelable {
 
     /** @hide */
-    @IntDef(
-        flag = true,
-        value = {
-                TYPE_BUILTIN,
-                TYPE_DYNAMIC,
-                TYPE_STATIC,
-        })
+    @IntDef(flag = true, prefix = { "TYPE_" }, value = {
+            TYPE_BUILTIN,
+            TYPE_DYNAMIC,
+            TYPE_STATIC,
+    })
     @Retention(RetentionPolicy.SOURCE)
     @interface Type{}
 
diff --git a/core/java/android/content/pm/ShortcutInfo.java b/core/java/android/content/pm/ShortcutInfo.java
index 9ff0775..8839cf9 100644
--- a/core/java/android/content/pm/ShortcutInfo.java
+++ b/core/java/android/content/pm/ShortcutInfo.java
@@ -109,8 +109,7 @@
     public static final int FLAG_SHADOW = 1 << 12;
 
     /** @hide */
-    @IntDef(flag = true,
-            value = {
+    @IntDef(flag = true, prefix = { "FLAG_" }, value = {
             FLAG_DYNAMIC,
             FLAG_PINNED,
             FLAG_HAS_ICON_RES,
@@ -153,15 +152,14 @@
             | CLONE_REMOVE_RES_NAMES;
 
     /** @hide */
-    @IntDef(flag = true,
-            value = {
-                    CLONE_REMOVE_ICON,
-                    CLONE_REMOVE_INTENT,
-                    CLONE_REMOVE_NON_KEY_INFO,
-                    CLONE_REMOVE_RES_NAMES,
-                    CLONE_REMOVE_FOR_CREATOR,
-                    CLONE_REMOVE_FOR_LAUNCHER
-            })
+    @IntDef(flag = true, prefix = { "CLONE_" }, value = {
+            CLONE_REMOVE_ICON,
+            CLONE_REMOVE_INTENT,
+            CLONE_REMOVE_NON_KEY_INFO,
+            CLONE_REMOVE_RES_NAMES,
+            CLONE_REMOVE_FOR_CREATOR,
+            CLONE_REMOVE_FOR_LAUNCHER
+    })
     @Retention(RetentionPolicy.SOURCE)
     public @interface CloneFlags {}
 
@@ -212,7 +210,7 @@
     public static final int DISABLED_REASON_OTHER_RESTORE_ISSUE = 103;
 
     /** @hide */
-    @IntDef(value = {
+    @IntDef(prefix = { "DISABLED_REASON_" }, value = {
             DISABLED_REASON_NOT_DISABLED,
             DISABLED_REASON_BY_APP,
             DISABLED_REASON_APP_CHANGED,
@@ -220,7 +218,7 @@
             DISABLED_REASON_BACKUP_NOT_SUPPORTED,
             DISABLED_REASON_SIGNATURE_MISMATCH,
             DISABLED_REASON_OTHER_RESTORE_ISSUE,
-            })
+    })
     @Retention(RetentionPolicy.SOURCE)
     public @interface DisabledReason{}
 
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index 26efda1..eb30979 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -781,25 +781,24 @@
     public int seq;
 
     /** @hide */
-    @IntDef(flag = true,
-            value = {
-                    NATIVE_CONFIG_MCC,
-                    NATIVE_CONFIG_MNC,
-                    NATIVE_CONFIG_LOCALE,
-                    NATIVE_CONFIG_TOUCHSCREEN,
-                    NATIVE_CONFIG_KEYBOARD,
-                    NATIVE_CONFIG_KEYBOARD_HIDDEN,
-                    NATIVE_CONFIG_NAVIGATION,
-                    NATIVE_CONFIG_ORIENTATION,
-                    NATIVE_CONFIG_DENSITY,
-                    NATIVE_CONFIG_SCREEN_SIZE,
-                    NATIVE_CONFIG_VERSION,
-                    NATIVE_CONFIG_SCREEN_LAYOUT,
-                    NATIVE_CONFIG_UI_MODE,
-                    NATIVE_CONFIG_SMALLEST_SCREEN_SIZE,
-                    NATIVE_CONFIG_LAYOUTDIR,
-                    NATIVE_CONFIG_COLOR_MODE,
-            })
+    @IntDef(flag = true, prefix = { "NATIVE_CONFIG_" }, value = {
+            NATIVE_CONFIG_MCC,
+            NATIVE_CONFIG_MNC,
+            NATIVE_CONFIG_LOCALE,
+            NATIVE_CONFIG_TOUCHSCREEN,
+            NATIVE_CONFIG_KEYBOARD,
+            NATIVE_CONFIG_KEYBOARD_HIDDEN,
+            NATIVE_CONFIG_NAVIGATION,
+            NATIVE_CONFIG_ORIENTATION,
+            NATIVE_CONFIG_DENSITY,
+            NATIVE_CONFIG_SCREEN_SIZE,
+            NATIVE_CONFIG_VERSION,
+            NATIVE_CONFIG_SCREEN_LAYOUT,
+            NATIVE_CONFIG_UI_MODE,
+            NATIVE_CONFIG_SMALLEST_SCREEN_SIZE,
+            NATIVE_CONFIG_LAYOUTDIR,
+            NATIVE_CONFIG_COLOR_MODE,
+    })
     @Retention(RetentionPolicy.SOURCE)
     public @interface NativeConfig {}
 
diff --git a/core/java/android/content/res/GradientColor.java b/core/java/android/content/res/GradientColor.java
index e465961..35ad503 100644
--- a/core/java/android/content/res/GradientColor.java
+++ b/core/java/android/content/res/GradientColor.java
@@ -75,9 +75,14 @@
 
     private static final boolean DBG_GRADIENT = false;
 
-    @IntDef({TILE_MODE_CLAMP, TILE_MODE_REPEAT, TILE_MODE_MIRROR})
+    @IntDef(prefix = { "TILE_MODE_" }, value = {
+            TILE_MODE_CLAMP,
+            TILE_MODE_REPEAT,
+            TILE_MODE_MIRROR
+    })
     @Retention(RetentionPolicy.SOURCE)
     private @interface GradientTileMode {}
+
     private static final int TILE_MODE_CLAMP = 0;
     private static final int TILE_MODE_REPEAT = 1;
     private static final int TILE_MODE_MIRROR = 2;
diff --git a/core/java/android/hardware/HardwareBuffer.java b/core/java/android/hardware/HardwareBuffer.java
index b111ad3..7866b52 100644
--- a/core/java/android/hardware/HardwareBuffer.java
+++ b/core/java/android/hardware/HardwareBuffer.java
@@ -42,7 +42,15 @@
 public final class HardwareBuffer implements Parcelable, AutoCloseable {
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef({RGBA_8888, RGBA_FP16, RGBA_1010102, RGBX_8888, RGB_888, RGB_565, BLOB})
+    @IntDef(prefix = { "RGB", "BLOB" }, value = {
+            RGBA_8888,
+            RGBA_FP16,
+            RGBA_1010102,
+            RGBX_8888,
+            RGB_888,
+            RGB_565,
+            BLOB
+    })
     public @interface Format {
     }
 
diff --git a/core/java/android/hardware/SensorAdditionalInfo.java b/core/java/android/hardware/SensorAdditionalInfo.java
index 7c876cf..5ff627f 100644
--- a/core/java/android/hardware/SensorAdditionalInfo.java
+++ b/core/java/android/hardware/SensorAdditionalInfo.java
@@ -68,8 +68,15 @@
      *
      * @hide
      */
-    @IntDef({TYPE_FRAME_BEGIN, TYPE_FRAME_END, TYPE_UNTRACKED_DELAY, TYPE_INTERNAL_TEMPERATURE,
-             TYPE_VEC3_CALIBRATION, TYPE_SENSOR_PLACEMENT, TYPE_SAMPLING})
+    @IntDef(prefix = { "TYPE_" }, value = {
+            TYPE_FRAME_BEGIN,
+            TYPE_FRAME_END,
+            TYPE_UNTRACKED_DELAY,
+            TYPE_INTERNAL_TEMPERATURE,
+            TYPE_VEC3_CALIBRATION,
+            TYPE_SENSOR_PLACEMENT,
+            TYPE_SAMPLING
+    })
     @Retention(RetentionPolicy.SOURCE)
     public @interface AdditionalInfoType {}
 
diff --git a/core/java/android/hardware/SensorDirectChannel.java b/core/java/android/hardware/SensorDirectChannel.java
index 36607c9..214d3ec 100644
--- a/core/java/android/hardware/SensorDirectChannel.java
+++ b/core/java/android/hardware/SensorDirectChannel.java
@@ -40,8 +40,12 @@
 
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef(flag = true, value = {TYPE_MEMORY_FILE, TYPE_HARDWARE_BUFFER})
-    public @interface MemoryType {};
+    @IntDef(flag = true, prefix = { "TYPE_" }, value = {
+            TYPE_MEMORY_FILE,
+            TYPE_HARDWARE_BUFFER
+    })
+    public @interface MemoryType {}
+
     /**
      * Shared memory type ashmem, wrapped in MemoryFile object.
      *
@@ -60,8 +64,13 @@
 
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef(flag = true, value = {RATE_STOP, RATE_NORMAL, RATE_FAST, RATE_VERY_FAST})
-    public @interface RateLevel {};
+    @IntDef(flag = true, prefix = { "RATE_" }, value = {
+            RATE_STOP,
+            RATE_NORMAL,
+            RATE_FAST,
+            RATE_VERY_FAST
+    })
+    public @interface RateLevel {}
 
     /**
      * Sensor stopped (no event output).
diff --git a/core/java/android/hardware/camera2/CameraAccessException.java b/core/java/android/hardware/camera2/CameraAccessException.java
index f9b659c..d238797 100644
--- a/core/java/android/hardware/camera2/CameraAccessException.java
+++ b/core/java/android/hardware/camera2/CameraAccessException.java
@@ -16,7 +16,6 @@
 
 package android.hardware.camera2;
 
-import android.annotation.NonNull;
 import android.annotation.IntDef;
 import android.util.AndroidException;
 
@@ -81,15 +80,16 @@
      */
     public static final int CAMERA_DEPRECATED_HAL = 1000;
 
-     /** @hide */
-     @Retention(RetentionPolicy.SOURCE)
-     @IntDef(
-         {CAMERA_IN_USE,
-          MAX_CAMERAS_IN_USE,
-          CAMERA_DISABLED,
-          CAMERA_DISCONNECTED,
-          CAMERA_ERROR})
-     public @interface AccessError {};
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = { "CAMERA_", "MAX_CAMERAS_IN_USE" }, value = {
+            CAMERA_IN_USE,
+            MAX_CAMERAS_IN_USE,
+            CAMERA_DISABLED,
+            CAMERA_DISCONNECTED,
+            CAMERA_ERROR
+    })
+    public @interface AccessError {}
 
     // Make the eclipse warning about serializable exceptions go away
     private static final long serialVersionUID = 5630338637471475675L; // randomly generated
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index c531a89..1de8882 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -188,7 +188,11 @@
 
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef({SWITCH_STATE_UNKNOWN, SWITCH_STATE_OFF, SWITCH_STATE_ON})
+    @IntDef(prefix = { "SWITCH_STATE_" }, value = {
+            SWITCH_STATE_UNKNOWN,
+            SWITCH_STATE_OFF,
+            SWITCH_STATE_ON
+    })
     public @interface SwitchState {}
 
     /**
diff --git a/core/java/android/hardware/location/ContextHubTransaction.java b/core/java/android/hardware/location/ContextHubTransaction.java
index b808de3..2a66cbc 100644
--- a/core/java/android/hardware/location/ContextHubTransaction.java
+++ b/core/java/android/hardware/location/ContextHubTransaction.java
@@ -47,13 +47,15 @@
      * Constants describing the type of a transaction through the Context Hub Service.
      */
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef({
+    @IntDef(prefix = { "TYPE_" }, value = {
             TYPE_LOAD_NANOAPP,
             TYPE_UNLOAD_NANOAPP,
             TYPE_ENABLE_NANOAPP,
             TYPE_DISABLE_NANOAPP,
-            TYPE_QUERY_NANOAPPS})
-    public @interface Type {}
+            TYPE_QUERY_NANOAPPS
+    })
+    public @interface Type { }
+
     public static final int TYPE_LOAD_NANOAPP = 0;
     public static final int TYPE_UNLOAD_NANOAPP = 1;
     public static final int TYPE_ENABLE_NANOAPP = 2;
@@ -64,7 +66,7 @@
      * Constants describing the result of a transaction or request through the Context Hub Service.
      */
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef({
+    @IntDef(prefix = { "TRANSACTION_" }, value = {
             TRANSACTION_SUCCESS,
             TRANSACTION_FAILED_UNKNOWN,
             TRANSACTION_FAILED_BAD_PARAMS,
@@ -73,7 +75,8 @@
             TRANSACTION_FAILED_AT_HUB,
             TRANSACTION_FAILED_TIMEOUT,
             TRANSACTION_FAILED_SERVICE_INTERNAL_FAILURE,
-            TRANSACTION_FAILED_HAL_UNAVAILABLE})
+            TRANSACTION_FAILED_HAL_UNAVAILABLE
+    })
     public @interface Result {}
     public static final int TRANSACTION_SUCCESS = 0;
     /**
diff --git a/core/java/android/os/HardwarePropertiesManager.java b/core/java/android/os/HardwarePropertiesManager.java
index aad202e..eae7d70 100644
--- a/core/java/android/os/HardwarePropertiesManager.java
+++ b/core/java/android/os/HardwarePropertiesManager.java
@@ -40,9 +40,11 @@
      * @hide
      */
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef({
-        DEVICE_TEMPERATURE_CPU, DEVICE_TEMPERATURE_GPU, DEVICE_TEMPERATURE_BATTERY,
-                DEVICE_TEMPERATURE_SKIN
+    @IntDef(prefix = { "DEVICE_TEMPERATURE_" }, value = {
+            DEVICE_TEMPERATURE_CPU,
+            DEVICE_TEMPERATURE_GPU,
+            DEVICE_TEMPERATURE_BATTERY,
+            DEVICE_TEMPERATURE_SKIN
     })
     public @interface DeviceTemperatureType {}
 
@@ -50,9 +52,11 @@
      * @hide
      */
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef({
-        TEMPERATURE_CURRENT, TEMPERATURE_THROTTLING, TEMPERATURE_SHUTDOWN,
-                TEMPERATURE_THROTTLING_BELOW_VR_MIN
+    @IntDef(prefix = { "TEMPERATURE_" }, value = {
+            TEMPERATURE_CURRENT,
+            TEMPERATURE_THROTTLING,
+            TEMPERATURE_SHUTDOWN,
+            TEMPERATURE_THROTTLING_BELOW_VR_MIN
     })
     public @interface TemperatureSource {}
 
diff --git a/core/java/android/os/MessageQueue.java b/core/java/android/os/MessageQueue.java
index 624e28a..96e7a59 100644
--- a/core/java/android/os/MessageQueue.java
+++ b/core/java/android/os/MessageQueue.java
@@ -871,7 +871,11 @@
 
         /** @hide */
         @Retention(RetentionPolicy.SOURCE)
-        @IntDef(flag=true, value={EVENT_INPUT, EVENT_OUTPUT, EVENT_ERROR})
+        @IntDef(flag = true, prefix = { "EVENT_" }, value = {
+                EVENT_INPUT,
+                EVENT_OUTPUT,
+                EVENT_ERROR
+        })
         public @interface Events {}
 
         /**
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 01fe5bf..56c6391 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -466,7 +466,7 @@
      * @hide
      */
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef({
+    @IntDef(prefix = { "SHUTDOWN_REASON_" }, value = {
             SHUTDOWN_REASON_UNKNOWN,
             SHUTDOWN_REASON_SHUTDOWN,
             SHUTDOWN_REASON_REBOOT,
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 61dd462..fb60bbb 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -103,8 +103,12 @@
 
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef(flag=true, value={RESTRICTION_NOT_SET, RESTRICTION_SOURCE_SYSTEM,
-            RESTRICTION_SOURCE_DEVICE_OWNER, RESTRICTION_SOURCE_PROFILE_OWNER})
+    @IntDef(flag = true, prefix = { "RESTRICTION_" }, value = {
+            RESTRICTION_NOT_SET,
+            RESTRICTION_SOURCE_SYSTEM,
+            RESTRICTION_SOURCE_DEVICE_OWNER,
+            RESTRICTION_SOURCE_PROFILE_OWNER
+    })
     @SystemApi
     public @interface UserRestrictionSource {}
 
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 4796712..9833fe1 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -1690,7 +1690,7 @@
     public static final int FLAG_ALLOCATE_DEFY_HALF_RESERVED = 1 << 2;
 
     /** @hide */
-    @IntDef(flag = true, value = {
+    @IntDef(flag = true, prefix = { "FLAG_ALLOCATE_" }, value = {
             FLAG_ALLOCATE_AGGRESSIVE,
             FLAG_ALLOCATE_DEFY_ALL_RESERVED,
             FLAG_ALLOCATE_DEFY_HALF_RESERVED,
diff --git a/core/java/android/print/PrintAttributes.java b/core/java/android/print/PrintAttributes.java
index ce5b11e..f0d9a0c 100644
--- a/core/java/android/print/PrintAttributes.java
+++ b/core/java/android/print/PrintAttributes.java
@@ -49,8 +49,9 @@
 public final class PrintAttributes implements Parcelable {
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef(flag = true, value = {
-            COLOR_MODE_MONOCHROME, COLOR_MODE_COLOR
+    @IntDef(flag = true, prefix = { "COLOR_MODE_" }, value = {
+            COLOR_MODE_MONOCHROME,
+            COLOR_MODE_COLOR
     })
     @interface ColorMode {
     }
@@ -64,8 +65,10 @@
 
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef(flag = true, value = {
-            DUPLEX_MODE_NONE, DUPLEX_MODE_LONG_EDGE, DUPLEX_MODE_SHORT_EDGE
+    @IntDef(flag = true, prefix = { "DUPLEX_MODE_" }, value = {
+            DUPLEX_MODE_NONE,
+            DUPLEX_MODE_LONG_EDGE,
+            DUPLEX_MODE_SHORT_EDGE
     })
     @interface DuplexMode {
     }
diff --git a/core/java/android/print/PrintDocumentInfo.java b/core/java/android/print/PrintDocumentInfo.java
index 6143404..55c902e 100644
--- a/core/java/android/print/PrintDocumentInfo.java
+++ b/core/java/android/print/PrintDocumentInfo.java
@@ -83,11 +83,14 @@
 
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef({
-            CONTENT_TYPE_UNKNOWN, CONTENT_TYPE_DOCUMENT, CONTENT_TYPE_PHOTO
+    @IntDef(prefix = { "CONTENT_TYPE_" }, value = {
+            CONTENT_TYPE_UNKNOWN,
+            CONTENT_TYPE_DOCUMENT,
+            CONTENT_TYPE_PHOTO
     })
     public @interface ContentType {
     }
+
     /**
      * Content type: unknown.
      */
diff --git a/core/java/android/print/PrintJobInfo.java b/core/java/android/print/PrintJobInfo.java
index 94686a8..85fdd64 100644
--- a/core/java/android/print/PrintJobInfo.java
+++ b/core/java/android/print/PrintJobInfo.java
@@ -45,9 +45,14 @@
 public final class PrintJobInfo implements Parcelable {
 
     /** @hide */
-    @IntDef({
-            STATE_CREATED, STATE_QUEUED, STATE_STARTED, STATE_BLOCKED, STATE_COMPLETED,
-            STATE_FAILED, STATE_CANCELED
+    @IntDef(prefix = { "STATE_" }, value = {
+            STATE_CREATED,
+            STATE_QUEUED,
+            STATE_STARTED,
+            STATE_BLOCKED,
+            STATE_COMPLETED,
+            STATE_FAILED,
+            STATE_CANCELED
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface State {
diff --git a/core/java/android/print/PrinterInfo.java b/core/java/android/print/PrinterInfo.java
index 88feab7..e79cc65 100644
--- a/core/java/android/print/PrinterInfo.java
+++ b/core/java/android/print/PrinterInfo.java
@@ -53,12 +53,15 @@
 public final class PrinterInfo implements Parcelable {
 
     /** @hide */
-    @IntDef({
-            STATUS_IDLE, STATUS_BUSY, STATUS_UNAVAILABLE
+    @IntDef(prefix = { "STATUS_" }, value = {
+            STATUS_IDLE,
+            STATUS_BUSY,
+            STATUS_UNAVAILABLE
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface Status {
     }
+
     /** Printer status: the printer is idle and ready to print. */
     public static final int STATUS_IDLE = PrinterInfoProto.STATUS_IDLE;
 
diff --git a/core/java/android/provider/FontsContract.java b/core/java/android/provider/FontsContract.java
index d8540ff..a1d1c57 100644
--- a/core/java/android/provider/FontsContract.java
+++ b/core/java/android/provider/FontsContract.java
@@ -283,7 +283,11 @@
         public static final int STATUS_REJECTED = 3;
 
         /** @hide */
-        @IntDef({STATUS_OK, STATUS_WRONG_CERTIFICATES, STATUS_UNEXPECTED_DATA_PROVIDED})
+        @IntDef(prefix = { "STATUS_" }, value = {
+                STATUS_OK,
+                STATUS_WRONG_CERTIFICATES,
+                STATUS_UNEXPECTED_DATA_PROVIDED
+        })
         @Retention(RetentionPolicy.SOURCE)
         @interface FontResultStatus {}
 
@@ -438,9 +442,13 @@
         public static final int FAIL_REASON_MALFORMED_QUERY = Columns.RESULT_CODE_MALFORMED_QUERY;
 
         /** @hide */
-        @IntDef({ FAIL_REASON_PROVIDER_NOT_FOUND, FAIL_REASON_FONT_LOAD_ERROR,
-                FAIL_REASON_FONT_NOT_FOUND, FAIL_REASON_FONT_UNAVAILABLE,
-                FAIL_REASON_MALFORMED_QUERY })
+        @IntDef(prefix = { "FAIL_" }, value = {
+                FAIL_REASON_PROVIDER_NOT_FOUND,
+                FAIL_REASON_FONT_LOAD_ERROR,
+                FAIL_REASON_FONT_NOT_FOUND,
+                FAIL_REASON_FONT_UNAVAILABLE,
+                FAIL_REASON_MALFORMED_QUERY
+        })
         @Retention(RetentionPolicy.SOURCE)
         @interface FontRequestFailReason {}
 
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 94ac052..4ecbdf5 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1689,7 +1689,7 @@
 
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef({
+    @IntDef(prefix = { "RESET_MODE_" }, value = {
             RESET_MODE_PACKAGE_DEFAULTS,
             RESET_MODE_UNTRUSTED_DEFAULTS,
             RESET_MODE_UNTRUSTED_CHANGES,
diff --git a/core/java/android/service/autofill/FillEventHistory.java b/core/java/android/service/autofill/FillEventHistory.java
index facad2d..a527f16 100644
--- a/core/java/android/service/autofill/FillEventHistory.java
+++ b/core/java/android/service/autofill/FillEventHistory.java
@@ -226,12 +226,13 @@
         public static final int TYPE_CONTEXT_COMMITTED = 4;
 
         /** @hide */
-        @IntDef(
-                value = {TYPE_DATASET_SELECTED,
-                        TYPE_DATASET_AUTHENTICATION_SELECTED,
-                        TYPE_AUTHENTICATION_SELECTED,
-                        TYPE_SAVE_SHOWN,
-                        TYPE_CONTEXT_COMMITTED})
+        @IntDef(prefix = { "TYPE_" }, value = {
+                TYPE_DATASET_SELECTED,
+                TYPE_DATASET_AUTHENTICATION_SELECTED,
+                TYPE_AUTHENTICATION_SELECTED,
+                TYPE_SAVE_SHOWN,
+                TYPE_CONTEXT_COMMITTED
+        })
         @Retention(RetentionPolicy.SOURCE)
         @interface EventIds{}
 
diff --git a/core/java/android/service/autofill/FillRequest.java b/core/java/android/service/autofill/FillRequest.java
index ddc92f6..33619ac 100644
--- a/core/java/android/service/autofill/FillRequest.java
+++ b/core/java/android/service/autofill/FillRequest.java
@@ -69,9 +69,9 @@
     public static final int INVALID_REQUEST_ID = Integer.MIN_VALUE;
 
     /** @hide */
-    @IntDef(
-        flag = true,
-        value = {FLAG_MANUAL_REQUEST})
+    @IntDef(flag = true, prefix = { "FLAG_" }, value = {
+            FLAG_MANUAL_REQUEST
+    })
     @Retention(RetentionPolicy.SOURCE)
     @interface RequestFlags{}
 
diff --git a/core/java/android/service/autofill/FillResponse.java b/core/java/android/service/autofill/FillResponse.java
index 04db698..014d3e8 100644
--- a/core/java/android/service/autofill/FillResponse.java
+++ b/core/java/android/service/autofill/FillResponse.java
@@ -63,7 +63,7 @@
     public static final int FLAG_DISABLE_ACTIVITY_ONLY = 0x2;
 
     /** @hide */
-    @IntDef(flag = true, value = {
+    @IntDef(flag = true, prefix = { "FLAG_" }, value = {
             FLAG_TRACK_CONTEXT_COMMITED,
             FLAG_DISABLE_ACTIVITY_ONLY
     })
diff --git a/core/java/android/service/autofill/SaveInfo.java b/core/java/android/service/autofill/SaveInfo.java
index 0b50f07..a5a6177 100644
--- a/core/java/android/service/autofill/SaveInfo.java
+++ b/core/java/android/service/autofill/SaveInfo.java
@@ -198,23 +198,22 @@
     public static final int NEGATIVE_BUTTON_STYLE_REJECT = 1;
 
     /** @hide */
-    @IntDef(
-        value = {
-                NEGATIVE_BUTTON_STYLE_CANCEL,
-                NEGATIVE_BUTTON_STYLE_REJECT})
+    @IntDef(prefix = { "NEGATIVE_BUTTON_STYLE_" }, value = {
+            NEGATIVE_BUTTON_STYLE_CANCEL,
+            NEGATIVE_BUTTON_STYLE_REJECT
+    })
     @Retention(RetentionPolicy.SOURCE)
     @interface NegativeButtonStyle{}
 
     /** @hide */
-    @IntDef(
-       flag = true,
-       value = {
-               SAVE_DATA_TYPE_GENERIC,
-               SAVE_DATA_TYPE_PASSWORD,
-               SAVE_DATA_TYPE_ADDRESS,
-               SAVE_DATA_TYPE_CREDIT_CARD,
-               SAVE_DATA_TYPE_USERNAME,
-               SAVE_DATA_TYPE_EMAIL_ADDRESS})
+    @IntDef(flag = true, prefix = { "SAVE_DATA_TYPE_" }, value = {
+            SAVE_DATA_TYPE_GENERIC,
+            SAVE_DATA_TYPE_PASSWORD,
+            SAVE_DATA_TYPE_ADDRESS,
+            SAVE_DATA_TYPE_CREDIT_CARD,
+            SAVE_DATA_TYPE_USERNAME,
+            SAVE_DATA_TYPE_EMAIL_ADDRESS
+    })
     @Retention(RetentionPolicy.SOURCE)
     @interface SaveDataType{}
 
@@ -235,9 +234,10 @@
     public static final int FLAG_DONT_SAVE_ON_FINISH = 0x2;
 
     /** @hide */
-    @IntDef(
-            flag = true,
-            value = {FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE, FLAG_DONT_SAVE_ON_FINISH})
+    @IntDef(flag = true, prefix = { "FLAG_" }, value = {
+            FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE,
+            FLAG_DONT_SAVE_ON_FINISH
+    })
     @Retention(RetentionPolicy.SOURCE)
     @interface SaveInfoFlags{}
 
diff --git a/core/java/android/service/notification/Condition.java b/core/java/android/service/notification/Condition.java
index 447afe6..2a352ad 100644
--- a/core/java/android/service/notification/Condition.java
+++ b/core/java/android/service/notification/Condition.java
@@ -39,7 +39,12 @@
     public static final String SCHEME = "condition";
 
     /** @hide */
-    @IntDef({STATE_FALSE, STATE_TRUE, STATE_TRUE, STATE_ERROR})
+    @IntDef(prefix = { "STATE_" }, value = {
+            STATE_FALSE,
+            STATE_TRUE,
+            STATE_UNKNOWN,
+            STATE_ERROR
+    })
     @Retention(RetentionPolicy.SOURCE)
     public @interface State {}
 
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index dac663e7..18d4a1e 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -229,8 +229,11 @@
 
 
     /** @hide */
-    @IntDef({NOTIFICATION_CHANNEL_OR_GROUP_ADDED, NOTIFICATION_CHANNEL_OR_GROUP_UPDATED,
-            NOTIFICATION_CHANNEL_OR_GROUP_DELETED})
+    @IntDef(prefix = { "NOTIFICATION_CHANNEL_OR_GROUP_" }, value = {
+            NOTIFICATION_CHANNEL_OR_GROUP_ADDED,
+            NOTIFICATION_CHANNEL_OR_GROUP_UPDATED,
+            NOTIFICATION_CHANNEL_OR_GROUP_DELETED
+    })
     @Retention(RetentionPolicy.SOURCE)
     public @interface ChannelOrGroupModificationTypes {}
 
diff --git a/core/java/android/service/persistentdata/PersistentDataBlockManager.java b/core/java/android/service/persistentdata/PersistentDataBlockManager.java
index 9332a5b..0bf68b7 100644
--- a/core/java/android/service/persistentdata/PersistentDataBlockManager.java
+++ b/core/java/android/service/persistentdata/PersistentDataBlockManager.java
@@ -65,10 +65,10 @@
      */
     public static final int FLASH_LOCK_LOCKED = 1;
 
-    @IntDef({
-        FLASH_LOCK_UNKNOWN,
-        FLASH_LOCK_LOCKED,
-        FLASH_LOCK_UNLOCKED,
+    @IntDef(prefix = { "FLASH_LOCK_" }, value = {
+            FLASH_LOCK_UNKNOWN,
+            FLASH_LOCK_LOCKED,
+            FLASH_LOCK_UNLOCKED,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface FlashLockState {}
diff --git a/core/java/android/service/settings/suggestions/Suggestion.java b/core/java/android/service/settings/suggestions/Suggestion.java
index cfeb7fc..11e1e67 100644
--- a/core/java/android/service/settings/suggestions/Suggestion.java
+++ b/core/java/android/service/settings/suggestions/Suggestion.java
@@ -38,7 +38,7 @@
     /**
      * @hide
      */
-    @IntDef(flag = true, value = {
+    @IntDef(flag = true, prefix = { "FLAG_" }, value = {
             FLAG_HAS_BUTTON,
     })
     @Retention(RetentionPolicy.SOURCE)
diff --git a/core/java/android/service/trust/TrustAgentService.java b/core/java/android/service/trust/TrustAgentService.java
index 5ef934e..4bade9f 100644
--- a/core/java/android/service/trust/TrustAgentService.java
+++ b/core/java/android/service/trust/TrustAgentService.java
@@ -114,11 +114,10 @@
 
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef(flag = true,
-            value = {
-                    FLAG_GRANT_TRUST_INITIATED_BY_USER,
-                    FLAG_GRANT_TRUST_DISMISS_KEYGUARD,
-            })
+    @IntDef(flag = true, prefix = { "FLAG_GRANT_TRUST_" }, value = {
+            FLAG_GRANT_TRUST_INITIATED_BY_USER,
+            FLAG_GRANT_TRUST_DISMISS_KEYGUARD,
+    })
     public @interface GrantTrustFlags {}
 
 
@@ -138,11 +137,10 @@
 
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef(flag = true,
-            value = {
-                TOKEN_STATE_ACTIVE,
-                TOKEN_STATE_INACTIVE,
-            })
+    @IntDef(flag = true, prefix = { "TOKEN_STATE_" }, value = {
+            TOKEN_STATE_ACTIVE,
+            TOKEN_STATE_INACTIVE,
+    })
     public @interface TokenState {}
 
     private static final int MSG_UNLOCK_ATTEMPT = 1;
diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
index 9464a87..76d89ef 100644
--- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java
+++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
@@ -87,11 +87,11 @@
 
     // Keyphrase management actions. Used in getManageIntent() ----//
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef(value = {
-                MANAGE_ACTION_ENROLL,
-                MANAGE_ACTION_RE_ENROLL,
-                MANAGE_ACTION_UN_ENROLL
-            })
+    @IntDef(prefix = { "MANAGE_ACTION_" }, value = {
+            MANAGE_ACTION_ENROLL,
+            MANAGE_ACTION_RE_ENROLL,
+            MANAGE_ACTION_UN_ENROLL
+    })
     private @interface ManageActions {}
 
     /**
@@ -116,12 +116,11 @@
     //-- Flags for startRecognition    ----//
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef(flag = true,
-            value = {
-                RECOGNITION_FLAG_NONE,
-                RECOGNITION_FLAG_CAPTURE_TRIGGER_AUDIO,
-                RECOGNITION_FLAG_ALLOW_MULTIPLE_TRIGGERS
-            })
+    @IntDef(flag = true, prefix = { "RECOGNITION_FLAG_" }, value = {
+            RECOGNITION_FLAG_NONE,
+            RECOGNITION_FLAG_CAPTURE_TRIGGER_AUDIO,
+            RECOGNITION_FLAG_ALLOW_MULTIPLE_TRIGGERS
+    })
     public @interface RecognitionFlags {}
 
     /**
@@ -150,11 +149,10 @@
 
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef(flag = true,
-            value = {
-                RECOGNITION_MODE_VOICE_TRIGGER,
-                RECOGNITION_MODE_USER_IDENTIFICATION,
-            })
+    @IntDef(flag = true, prefix = { "RECOGNITION_MODE_" }, value = {
+            RECOGNITION_MODE_VOICE_TRIGGER,
+            RECOGNITION_MODE_USER_IDENTIFICATION,
+    })
     public @interface RecognitionModes {}
 
     /**
diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java
index 763ea2c..01562b3 100644
--- a/core/java/android/speech/tts/TextToSpeech.java
+++ b/core/java/android/speech/tts/TextToSpeech.java
@@ -80,8 +80,15 @@
     public static final int STOPPED = -2;
 
     /** @hide */
-    @IntDef({ERROR_SYNTHESIS, ERROR_SERVICE, ERROR_OUTPUT, ERROR_NETWORK, ERROR_NETWORK_TIMEOUT,
-             ERROR_INVALID_REQUEST, ERROR_NOT_INSTALLED_YET})
+    @IntDef(prefix = { "ERROR_" }, value = {
+            ERROR_SYNTHESIS,
+            ERROR_SERVICE,
+            ERROR_OUTPUT,
+            ERROR_NETWORK,
+            ERROR_NETWORK_TIMEOUT,
+            ERROR_INVALID_REQUEST,
+            ERROR_NOT_INSTALLED_YET
+    })
     @Retention(RetentionPolicy.SOURCE)
     public @interface Error {}
 
diff --git a/core/java/android/text/FontConfig.java b/core/java/android/text/FontConfig.java
index 4654e83..7386e3e 100644
--- a/core/java/android/text/FontConfig.java
+++ b/core/java/android/text/FontConfig.java
@@ -179,7 +179,11 @@
 
         /** @hide */
         @Retention(SOURCE)
-        @IntDef({VARIANT_DEFAULT, VARIANT_COMPACT, VARIANT_ELEGANT})
+        @IntDef(prefix = { "VARIANT_" }, value = {
+                VARIANT_DEFAULT,
+                VARIANT_COMPACT,
+                VARIANT_ELEGANT
+        })
         public @interface Variant {}
 
         /**
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index 2a693a1..bf4b6ac 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -48,7 +48,11 @@
  */
 public abstract class Layout {
     /** @hide */
-    @IntDef({BREAK_STRATEGY_SIMPLE, BREAK_STRATEGY_HIGH_QUALITY, BREAK_STRATEGY_BALANCED})
+    @IntDef(prefix = { "BREAK_STRATEGY_" }, value = {
+            BREAK_STRATEGY_SIMPLE,
+            BREAK_STRATEGY_HIGH_QUALITY,
+            BREAK_STRATEGY_BALANCED
+    })
     @Retention(RetentionPolicy.SOURCE)
     public @interface BreakStrategy {}
 
@@ -73,8 +77,11 @@
     public static final int BREAK_STRATEGY_BALANCED = 2;
 
     /** @hide */
-    @IntDef({HYPHENATION_FREQUENCY_NORMAL, HYPHENATION_FREQUENCY_FULL,
-             HYPHENATION_FREQUENCY_NONE})
+    @IntDef(prefix = { "HYPHENATION_FREQUENCY_" }, value = {
+            HYPHENATION_FREQUENCY_NORMAL,
+            HYPHENATION_FREQUENCY_FULL,
+            HYPHENATION_FREQUENCY_NONE
+    })
     @Retention(RetentionPolicy.SOURCE)
     public @interface HyphenationFrequency {}
 
@@ -105,7 +112,10 @@
         ArrayUtils.emptyArray(ParagraphStyle.class);
 
     /** @hide */
-    @IntDef({JUSTIFICATION_MODE_NONE, JUSTIFICATION_MODE_INTER_WORD})
+    @IntDef(prefix = { "JUSTIFICATION_MODE_" }, value = {
+            JUSTIFICATION_MODE_NONE,
+            JUSTIFICATION_MODE_INTER_WORD
+    })
     @Retention(RetentionPolicy.SOURCE)
     public @interface JustificationMode {}
 
@@ -2267,7 +2277,10 @@
     private int mJustificationMode;
 
     /** @hide */
-    @IntDef({DIR_LEFT_TO_RIGHT, DIR_RIGHT_TO_LEFT})
+    @IntDef(prefix = { "DIR_" }, value = {
+            DIR_LEFT_TO_RIGHT,
+            DIR_RIGHT_TO_LEFT
+    })
     @Retention(RetentionPolicy.SOURCE)
     public @interface Direction {}
 
@@ -2308,7 +2321,10 @@
 
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef({TEXT_SELECTION_LAYOUT_RIGHT_TO_LEFT, TEXT_SELECTION_LAYOUT_LEFT_TO_RIGHT})
+    @IntDef(prefix = { "TEXT_SELECTION_LAYOUT_" }, value = {
+            TEXT_SELECTION_LAYOUT_RIGHT_TO_LEFT,
+            TEXT_SELECTION_LAYOUT_LEFT_TO_RIGHT
+    })
     public @interface TextSelectionLayout {}
 
     /** @hide */
diff --git a/core/java/android/transition/Visibility.java b/core/java/android/transition/Visibility.java
index 5b851ca..f0838a1 100644
--- a/core/java/android/transition/Visibility.java
+++ b/core/java/android/transition/Visibility.java
@@ -52,7 +52,10 @@
 
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef(flag=true, value={MODE_IN, MODE_OUT})
+    @IntDef(flag = true, prefix = { "MODE_" }, value = {
+            MODE_IN,
+            MODE_OUT
+    })
     @interface VisibilityMode {}
 
     /**
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 9673be0..5bd7446 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -1323,10 +1323,10 @@
         public static final int HDR_TYPE_HLG = 3;
 
         /** @hide */
-        @IntDef({
-            HDR_TYPE_DOLBY_VISION,
-            HDR_TYPE_HDR10,
-            HDR_TYPE_HLG,
+        @IntDef(prefix = { "HDR_TYPE_" }, value = {
+                HDR_TYPE_DOLBY_VISION,
+                HDR_TYPE_HDR10,
+                HDR_TYPE_HLG,
         })
         @Retention(RetentionPolicy.SOURCE)
         public @interface HdrType {}
diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java
index ddced6c..a417a4a 100644
--- a/core/java/android/view/Surface.java
+++ b/core/java/android/view/Surface.java
@@ -121,8 +121,12 @@
 
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef({SCALING_MODE_FREEZE, SCALING_MODE_SCALE_TO_WINDOW,
-                    SCALING_MODE_SCALE_CROP, SCALING_MODE_NO_SCALE_CROP})
+    @IntDef(prefix = { "SCALING_MODE_" }, value = {
+            SCALING_MODE_FREEZE,
+            SCALING_MODE_SCALE_TO_WINDOW,
+            SCALING_MODE_SCALE_CROP,
+            SCALING_MODE_NO_SCALE_CROP
+    })
     public @interface ScalingMode {}
     // From system/window.h
     /** @hide */
@@ -135,7 +139,12 @@
     public static final int SCALING_MODE_NO_SCALE_CROP = 3;
 
     /** @hide */
-    @IntDef({ROTATION_0, ROTATION_90, ROTATION_180, ROTATION_270})
+    @IntDef(prefix = { "ROTATION_" }, value = {
+            ROTATION_0,
+            ROTATION_90,
+            ROTATION_180,
+            ROTATION_270
+    })
     @Retention(RetentionPolicy.SOURCE)
     public @interface Rotation {}
 
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 357b8d9..268e460 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -30,6 +30,7 @@
 import android.graphics.GraphicBuffer;
 import android.graphics.PixelFormat;
 import android.graphics.Matrix;
+import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.os.IBinder;
@@ -37,8 +38,12 @@
 import android.os.Parcelable;
 import android.os.Process;
 import android.os.UserHandle;
+import android.util.ArrayMap;
 import android.util.Log;
 import android.view.Surface.OutOfResourcesException;
+
+import com.android.internal.annotations.GuardedBy;
+
 import dalvik.system.CloseGuard;
 import libcore.util.NativeAllocationRegistry;
 
@@ -153,6 +158,13 @@
     private final String mName;
     long mNativeObject; // package visibility only for Surface.java access
 
+    // TODO: Move this to native.
+    private final Object mSizeLock = new Object();
+    @GuardedBy("mSizeLock")
+    private int mWidth;
+    @GuardedBy("mSizeLock")
+    private int mHeight;
+
     static Transaction sGlobalTransaction;
     static long sTransactionNestCount = 0;
 
@@ -567,6 +579,8 @@
         }
 
         mName = name;
+        mWidth = w;
+        mHeight = h;
         mNativeObject = nativeCreate(session, name, w, h, format, flags,
             parent != null ? parent.mNativeObject : 0, windowType, ownerUid);
         if (mNativeObject == 0) {
@@ -582,6 +596,8 @@
     // event logging.
     public SurfaceControl(SurfaceControl other) {
         mName = other.mName;
+        mWidth = other.mWidth;
+        mHeight = other.mHeight;
         mNativeObject = other.mNativeObject;
         other.mCloseGuard.close();
         other.mNativeObject = 0;
@@ -590,6 +606,8 @@
 
     private SurfaceControl(Parcel in) {
         mName = in.readString();
+        mWidth = in.readInt();
+        mHeight = in.readInt();
         mNativeObject = nativeReadFromParcel(in);
         if (mNativeObject == 0) {
             throw new IllegalArgumentException("Couldn't read SurfaceControl from parcel=" + in);
@@ -605,6 +623,8 @@
     @Override
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeString(mName);
+        dest.writeInt(mWidth);
+        dest.writeInt(mHeight);
         nativeWriteToParcel(mNativeObject, dest);
     }
 
@@ -922,6 +942,18 @@
         }
     }
 
+    public int getWidth() {
+        synchronized (mSizeLock) {
+            return mWidth;
+        }
+    }
+
+    public int getHeight() {
+        synchronized (mSizeLock) {
+            return mHeight;
+        }
+    }
+
     @Override
     public String toString() {
         return "Surface(name=" + mName + ")/@0x" +
@@ -1282,6 +1314,7 @@
                 nativeGetNativeTransactionFinalizer(), 512);
         private long mNativeObject;
 
+        private final ArrayMap<SurfaceControl, Point> mResizedSurfaces = new ArrayMap<>();
         Runnable mFreeNativeResources;
 
         public Transaction() {
@@ -1312,9 +1345,22 @@
          * Jankier version of apply. Avoid use (b/28068298).
          */
         public void apply(boolean sync) {
+            applyResizedSurfaces();
             nativeApplyTransaction(mNativeObject, sync);
         }
 
+        private void applyResizedSurfaces() {
+            for (int i = mResizedSurfaces.size() - 1; i >= 0; i--) {
+                final Point size = mResizedSurfaces.valueAt(i);
+                final SurfaceControl surfaceControl = mResizedSurfaces.keyAt(i);
+                synchronized (surfaceControl.mSizeLock) {
+                    surfaceControl.mWidth = size.x;
+                    surfaceControl.mHeight = size.y;
+                }
+            }
+            mResizedSurfaces.clear();
+        }
+
         public Transaction show(SurfaceControl sc) {
             sc.checkNotReleased();
             nativeSetFlags(mNativeObject, sc.mNativeObject, 0, SURFACE_HIDDEN);
@@ -1335,6 +1381,7 @@
 
         public Transaction setSize(SurfaceControl sc, int w, int h) {
             sc.checkNotReleased();
+            mResizedSurfaces.put(sc, new Point(w, h));
             nativeSetSize(mNativeObject, sc.mNativeObject, w, h);
             return this;
         }
@@ -1567,6 +1614,8 @@
          * other transaction as if it had been applied.
          */
         public Transaction merge(Transaction other) {
+            mResizedSurfaces.putAll(other.mResizedSurfaces);
+            other.mResizedSurfaces.clear();
             nativeMergeTransaction(mNativeObject, other.mNativeObject);
             return this;
         }
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 0a69772..6a8f8b1 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -344,8 +344,10 @@
     private static final int FLAG_DUMP_FRAMESTATS   = 1 << 0;
     private static final int FLAG_DUMP_RESET        = 1 << 1;
 
-    @IntDef(flag = true, value = {
-            FLAG_DUMP_FRAMESTATS, FLAG_DUMP_RESET })
+    @IntDef(flag = true, prefix = { "FLAG_DUMP_" }, value = {
+            FLAG_DUMP_FRAMESTATS,
+            FLAG_DUMP_RESET
+    })
     @Retention(RetentionPolicy.SOURCE)
     public @interface DumpFlags {}
 
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 623759e..ab5372a 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -1178,7 +1178,7 @@
     private AutofillId mAutofillId;
 
     /** @hide */
-    @IntDef({
+    @IntDef(prefix = { "AUTOFILL_TYPE_" }, value = {
             AUTOFILL_TYPE_NONE,
             AUTOFILL_TYPE_TEXT,
             AUTOFILL_TYPE_TOGGLE,
@@ -1249,7 +1249,7 @@
     public static final int AUTOFILL_TYPE_DATE = 4;
 
     /** @hide */
-    @IntDef({
+    @IntDef(prefix = { "IMPORTANT_FOR_AUTOFILL_" }, value = {
             IMPORTANT_FOR_AUTOFILL_AUTO,
             IMPORTANT_FOR_AUTOFILL_YES,
             IMPORTANT_FOR_AUTOFILL_NO,
@@ -1300,9 +1300,9 @@
     public static final int IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS = 0x8;
 
     /** @hide */
-    @IntDef(
-            flag = true,
-            value = {AUTOFILL_FLAG_INCLUDE_NOT_IMPORTANT_VIEWS})
+    @IntDef(flag = true, prefix = { "AUTOFILL_FLAG_" }, value = {
+            AUTOFILL_FLAG_INCLUDE_NOT_IMPORTANT_VIEWS
+    })
     @Retention(RetentionPolicy.SOURCE)
     public @interface AutofillFlags {}
 
@@ -1452,7 +1452,11 @@
 
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef({DRAWING_CACHE_QUALITY_LOW, DRAWING_CACHE_QUALITY_HIGH, DRAWING_CACHE_QUALITY_AUTO})
+    @IntDef(prefix = { "DRAWING_CACHE_QUALITY_" }, value = {
+            DRAWING_CACHE_QUALITY_LOW,
+            DRAWING_CACHE_QUALITY_HIGH,
+            DRAWING_CACHE_QUALITY_AUTO
+    })
     public @interface DrawingCacheQuality {}
 
     /**
@@ -1551,13 +1555,12 @@
      */
     static final int CONTEXT_CLICKABLE = 0x00800000;
 
-
     /** @hide */
-    @IntDef({
-        SCROLLBARS_INSIDE_OVERLAY,
-        SCROLLBARS_INSIDE_INSET,
-        SCROLLBARS_OUTSIDE_OVERLAY,
-        SCROLLBARS_OUTSIDE_INSET
+    @IntDef(prefix = { "SCROLLBARS_" }, value = {
+            SCROLLBARS_INSIDE_OVERLAY,
+            SCROLLBARS_INSIDE_INSET,
+            SCROLLBARS_OUTSIDE_OVERLAY,
+            SCROLLBARS_OUTSIDE_INSET
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface ScrollBarStyle {}
@@ -1651,11 +1654,10 @@
     static final int TOOLTIP = 0x40000000;
 
     /** @hide */
-    @IntDef(flag = true,
-            value = {
-                FOCUSABLES_ALL,
-                FOCUSABLES_TOUCH_MODE
-            })
+    @IntDef(flag = true, prefix = { "FOCUSABLES_" }, value = {
+            FOCUSABLES_ALL,
+            FOCUSABLES_TOUCH_MODE
+    })
     @Retention(RetentionPolicy.SOURCE)
     public @interface FocusableMode {}
 
@@ -1672,7 +1674,7 @@
     public static final int FOCUSABLES_TOUCH_MODE = 0x00000001;
 
     /** @hide */
-    @IntDef({
+    @IntDef(prefix = { "FOCUS_" }, value = {
             FOCUS_BACKWARD,
             FOCUS_FORWARD,
             FOCUS_LEFT,
@@ -1684,7 +1686,7 @@
     public @interface FocusDirection {}
 
     /** @hide */
-    @IntDef({
+    @IntDef(prefix = { "FOCUS_" }, value = {
             FOCUS_LEFT,
             FOCUS_UP,
             FOCUS_RIGHT,
@@ -2426,20 +2428,20 @@
     static final int PFLAG2_DRAG_HOVERED               = 0x00000002;
 
     /** @hide */
-    @IntDef({
-        LAYOUT_DIRECTION_LTR,
-        LAYOUT_DIRECTION_RTL,
-        LAYOUT_DIRECTION_INHERIT,
-        LAYOUT_DIRECTION_LOCALE
+    @IntDef(prefix = { "LAYOUT_DIRECTION_" }, value = {
+            LAYOUT_DIRECTION_LTR,
+            LAYOUT_DIRECTION_RTL,
+            LAYOUT_DIRECTION_INHERIT,
+            LAYOUT_DIRECTION_LOCALE
     })
     @Retention(RetentionPolicy.SOURCE)
     // Not called LayoutDirection to avoid conflict with android.util.LayoutDirection
     public @interface LayoutDir {}
 
     /** @hide */
-    @IntDef({
-        LAYOUT_DIRECTION_LTR,
-        LAYOUT_DIRECTION_RTL
+    @IntDef(prefix = { "LAYOUT_DIRECTION_" }, value = {
+            LAYOUT_DIRECTION_LTR,
+            LAYOUT_DIRECTION_RTL
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface ResolvedLayoutDir {}
@@ -2645,14 +2647,14 @@
             TEXT_DIRECTION_RESOLVED_DEFAULT << PFLAG2_TEXT_DIRECTION_RESOLVED_MASK_SHIFT;
 
     /** @hide */
-    @IntDef({
-        TEXT_ALIGNMENT_INHERIT,
-        TEXT_ALIGNMENT_GRAVITY,
-        TEXT_ALIGNMENT_CENTER,
-        TEXT_ALIGNMENT_TEXT_START,
-        TEXT_ALIGNMENT_TEXT_END,
-        TEXT_ALIGNMENT_VIEW_START,
-        TEXT_ALIGNMENT_VIEW_END
+    @IntDef(prefix = { "TEXT_ALIGNMENT_" }, value = {
+            TEXT_ALIGNMENT_INHERIT,
+            TEXT_ALIGNMENT_GRAVITY,
+            TEXT_ALIGNMENT_CENTER,
+            TEXT_ALIGNMENT_TEXT_START,
+            TEXT_ALIGNMENT_TEXT_END,
+            TEXT_ALIGNMENT_VIEW_START,
+            TEXT_ALIGNMENT_VIEW_END
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface TextAlignment {}
@@ -3049,15 +3051,14 @@
 
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef(flag = true,
-            value = {
-                    SCROLL_INDICATOR_TOP,
-                    SCROLL_INDICATOR_BOTTOM,
-                    SCROLL_INDICATOR_LEFT,
-                    SCROLL_INDICATOR_RIGHT,
-                    SCROLL_INDICATOR_START,
-                    SCROLL_INDICATOR_END,
-            })
+    @IntDef(flag = true, prefix = { "SCROLL_INDICATOR_" }, value = {
+            SCROLL_INDICATOR_TOP,
+            SCROLL_INDICATOR_BOTTOM,
+            SCROLL_INDICATOR_LEFT,
+            SCROLL_INDICATOR_RIGHT,
+            SCROLL_INDICATOR_START,
+            SCROLL_INDICATOR_END,
+    })
     public @interface ScrollIndicators {}
 
     /**
@@ -3683,8 +3684,10 @@
             | SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
 
     /** @hide */
-    @IntDef(flag = true,
-            value = { FIND_VIEWS_WITH_TEXT, FIND_VIEWS_WITH_CONTENT_DESCRIPTION })
+    @IntDef(flag = true, prefix = { "FIND_VIEWS_" }, value = {
+            FIND_VIEWS_WITH_TEXT,
+            FIND_VIEWS_WITH_CONTENT_DESCRIPTION
+    })
     @Retention(RetentionPolicy.SOURCE)
     public @interface FindViewFlags {}
 
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index ce3022e..012e864 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -1746,7 +1746,7 @@
          * @hide
          */
         @Retention(RetentionPolicy.SOURCE)
-        @IntDef(flag = true, value = {
+        @IntDef(flag = true, prefix = { "SOFT_INPUT_" }, value = {
                 SOFT_INPUT_STATE_UNSPECIFIED,
                 SOFT_INPUT_STATE_UNCHANGED,
                 SOFT_INPUT_STATE_HIDDEN,
diff --git a/core/java/android/view/accessibility/AccessibilityRequestPreparer.java b/core/java/android/view/accessibility/AccessibilityRequestPreparer.java
index 889feb9..25f830a 100644
--- a/core/java/android/view/accessibility/AccessibilityRequestPreparer.java
+++ b/core/java/android/view/accessibility/AccessibilityRequestPreparer.java
@@ -44,10 +44,9 @@
     public static final int REQUEST_TYPE_EXTRA_DATA = 0x00000001;
 
     /** @hide */
-    @IntDef(flag = true,
-            value = {
-                REQUEST_TYPE_EXTRA_DATA
-            })
+    @IntDef(flag = true, prefix = { "REQUEST_TYPE_" }, value = {
+            REQUEST_TYPE_EXTRA_DATA
+    })
     @Retention(RetentionPolicy.SOURCE)
     public @interface RequestTypes {}
 
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 419aeb3..6d84aa0 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -1987,7 +1987,11 @@
     public abstract static class AutofillCallback {
 
         /** @hide */
-        @IntDef({EVENT_INPUT_SHOWN, EVENT_INPUT_HIDDEN, EVENT_INPUT_UNAVAILABLE})
+        @IntDef(prefix = { "EVENT_INPUT_" }, value = {
+                EVENT_INPUT_SHOWN,
+                EVENT_INPUT_HIDDEN,
+                EVENT_INPUT_UNAVAILABLE
+        })
         @Retention(RetentionPolicy.SOURCE)
         public @interface AutofillEventType {}
 
diff --git a/core/java/android/webkit/TracingConfig.java b/core/java/android/webkit/TracingConfig.java
new file mode 100644
index 0000000..75e2bf7
--- /dev/null
+++ b/core/java/android/webkit/TracingConfig.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.webkit;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Holds tracing configuration information and predefined settings.
+ */
+public class TracingConfig {
+
+    private final String mCustomCategoryPattern;
+    private final @PresetCategories int mPresetCategories;
+    private @TracingMode int mTracingMode;
+
+    /** @hide */
+    @IntDef({CATEGORIES_NONE, CATEGORIES_WEB_DEVELOPER, CATEGORIES_INPUT_LATENCY,
+            CATEGORIES_RENDERING, CATEGORIES_JAVASCRIPT_AND_RENDERING, CATEGORIES_FRAME_VIEWER})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface PresetCategories {}
+
+    /**
+     * Indicates that there are no preset categories.
+     */
+    public static final int CATEGORIES_NONE = -1;
+
+    /**
+     * Predefined categories typically useful for web developers.
+     * Typically includes blink, compositor, renderer.scheduler and v8 categories.
+     */
+    public static final int CATEGORIES_WEB_DEVELOPER = 0;
+
+    /**
+     * Predefined categories for analyzing input latency issues.
+     * Typically includes input, renderer.scheduler categories.
+     */
+    public static final int CATEGORIES_INPUT_LATENCY = 1;
+
+    /**
+     * Predefined categories for analyzing rendering issues.
+     * Typically includes blink, compositor and gpu categories.
+     */
+    public static final int CATEGORIES_RENDERING = 2;
+
+    /**
+     * Predefined categories for analyzing javascript and rendering issues.
+     * Typically includes blink, compositor, gpu, renderer.schduler and v8 categories.
+     */
+    public static final int CATEGORIES_JAVASCRIPT_AND_RENDERING = 3;
+
+    /**
+     * Predefined categories for studying difficult rendering performance problems.
+     * Typically includes blink, compositor, gpu, renderer.scheduler, v8 and
+     * some other compositor categories which are disabled by default.
+     */
+    public static final int CATEGORIES_FRAME_VIEWER = 4;
+
+    /** @hide */
+    @IntDef({RECORD_UNTIL_FULL, RECORD_CONTINUOUSLY, RECORD_UNTIL_FULL_LARGE_BUFFER,
+            RECORD_TO_CONSOLE})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface TracingMode {}
+
+    /**
+     * Record trace events until the internal tracing buffer is full. Default tracing mode.
+     * Typically the buffer memory usage is between {@link #RECORD_CONTINUOUSLY} and the
+     * {@link #RECORD_UNTIL_FULL_LARGE_BUFFER}. Depending on the implementation typically allows
+     * up to 256k events to be stored.
+     */
+    public static final int RECORD_UNTIL_FULL = 0;
+
+    /**
+     * Record trace events continuously using an internal ring buffer. Overwrites
+     * old events if they exceed buffer capacity. Uses less memory than both
+     * {@link #RECORD_UNTIL_FULL} and {@link #RECORD_UNTIL_FULL_LARGE_BUFFER} modes.
+     * Depending on the implementation typically allows up to 64k events to be stored.
+     */
+    public static final int RECORD_CONTINUOUSLY = 1;
+
+    /**
+     * Record trace events using a larger internal tracing buffer until it is full.
+     * Uses more memory than the other modes and may not be suitable on devices
+     * with smaller RAM. Depending on the implementation typically allows up to
+     * 512 million events to be stored.
+     */
+    public static final int RECORD_UNTIL_FULL_LARGE_BUFFER = 2;
+
+    /**
+     * Record trace events to console (logcat). The events are discarded and nothing
+     * is sent back to the caller. Uses the least memory as compared to the other modes.
+     */
+    public static final int RECORD_TO_CONSOLE = 3;
+
+    /**
+     * Create config with the preset categories.
+     * <p>
+     * Example:
+     *    TracingConfig(CATEGORIES_WEB_DEVELOPER) -- records trace events from the "web developer"
+     *                                               preset categories.
+     *
+     * @param presetCategories preset categories to use, one of {@link #CATEGORIES_WEB_DEVELOPER},
+     *                    {@link #CATEGORIES_INPUT_LATENCY}, {@link #CATEGORIES_RENDERING},
+     *                    {@link #CATEGORIES_JAVASCRIPT_AND_RENDERING} or
+     *                    {@link #CATEGORIES_FRAME_VIEWER}.
+     *
+     * Note: for specifying custom categories without presets use
+     * {@link #TracingConfig(int, String, int)}.
+     *
+     */
+    public TracingConfig(@PresetCategories int presetCategories) {
+        this(presetCategories, "", RECORD_UNTIL_FULL);
+    }
+
+    /**
+     * Create a configuration with both preset categories and custom categories.
+     * Also allows to specify the tracing mode.
+     *
+     * Note that the categories are defined by the currently-in-use version of WebView. They live
+     * in chromium code and are not part of the Android API. See
+     * See <a href="https://www.chromium.org/developers/how-tos/trace-event-profiling-tool">
+     * chromium documentation on tracing</a> for more details.
+     *
+     * <p>
+     * Examples:
+     *
+     *  Preset category with a specified trace mode:
+     *    TracingConfig(CATEGORIES_WEB_DEVELOPER, "", RECORD_UNTIL_FULL_LARGE_BUFFER);
+     *  Custom categories:
+     *    TracingConfig(CATEGORIES_NONE, "browser", RECORD_UNTIL_FULL)
+     *      -- records only the trace events from the "browser" category.
+     *    TraceConfig(CATEGORIES_NONE, "-input,-gpu", RECORD_UNTIL_FULL)
+     *      -- records all trace events excluding the events from the "input" and 'gpu' categories.
+     *    TracingConfig(CATEGORIES_NONE, "blink*,devtools*", RECORD_UNTIL_FULL)
+     *      -- records only the trace events matching the "blink*" and "devtools*" patterns
+     *         (e.g. "blink_gc" and "devtools.timeline" categories).
+     *
+     *  Combination of preset and additional custom categories:
+     *    TracingConfig(CATEGORIES_WEB_DEVELOPER, "memory-infra", RECORD_CONTINUOUSLY)
+     *      -- records events from the "web developer" categories and events from the "memory-infra"
+     *         category to understand where memory is being used.
+     *
+     * @param presetCategories preset categories to use, one of {@link #CATEGORIES_WEB_DEVELOPER},
+     *                    {@link #CATEGORIES_INPUT_LATENCY}, {@link #CATEGORIES_RENDERING},
+     *                    {@link #CATEGORIES_JAVASCRIPT_AND_RENDERING} or
+     *                    {@link #CATEGORIES_FRAME_VIEWER}.
+     * @param customCategories a comma-delimited list of category wildcards. A category can
+     *                         have an optional '-' prefix to make it an excluded category.
+     * @param tracingMode tracing mode to use, one of {@link #RECORD_UNTIL_FULL},
+     *                    {@link #RECORD_CONTINUOUSLY}, {@link #RECORD_UNTIL_FULL_LARGE_BUFFER}
+     *                    or {@link #RECORD_TO_CONSOLE}.
+     */
+    public TracingConfig(@PresetCategories int presetCategories,
+            @NonNull String customCategories, @TracingMode int tracingMode) {
+        mPresetCategories = presetCategories;
+        mCustomCategoryPattern = customCategories;
+        mTracingMode = RECORD_UNTIL_FULL;
+    }
+
+    /**
+     * Returns the custom category pattern for this configuration.
+     *
+     * @return empty string if no custom category pattern is specified.
+     */
+    @NonNull
+    public String getCustomCategoryPattern() {
+        return mCustomCategoryPattern;
+    }
+
+    /**
+     * Returns the preset categories value of this configuration.
+     */
+    @PresetCategories
+    public int getPresetCategories() {
+        return mPresetCategories;
+    }
+
+    /**
+     * Returns the tracing mode of this configuration.
+     */
+    @TracingMode
+    public int getTracingMode() {
+        return mTracingMode;
+    }
+
+}
diff --git a/core/java/android/webkit/TracingController.java b/core/java/android/webkit/TracingController.java
new file mode 100644
index 0000000..cadb8a1
--- /dev/null
+++ b/core/java/android/webkit/TracingController.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.webkit;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Handler;
+
+/**
+ * Manages tracing of WebViews. In particular provides functionality for the app
+ * to enable/disable tracing of parts of code and to collect tracing data.
+ * This is useful for profiling performance issues, debugging and memory usage
+ * analysis in production and real life scenarios.
+ * <p>
+ * The resulting trace data is sent back as a byte sequence in json format. This
+ * file can be loaded in "chrome://tracing" for further analysis.
+ * <p>
+ * Note: All methods in this class must be called on the UI thread. All callbacks
+ * are also called on the UI thread.
+ * <p>
+ * Example usage:
+ * <pre class="prettyprint">
+ * TracingController tracingController = TracingController.getInstance();
+ * tracingController.start(new TraceConfig(CATEGORIES_WEB_DEVELOPER));
+ * [..]
+ * tracingController.stopAndFlush(new TraceFileOutput("trace.json"), null);
+ * </pre></p>
+ */
+public abstract class TracingController {
+
+    /**
+     * Interface for capturing tracing data.
+     */
+    public interface TracingOutputStream {
+        /**
+         * Will be called to return tracing data in chunks.
+         * Tracing data is returned in json format an array of bytes.
+         */
+        void write(byte[] chunk);
+
+        /**
+         * Called when tracing is finished and the data collection is over.
+         * There will be no calls to #write after #complete is called.
+         */
+        void complete();
+    }
+
+    /**
+     * Returns the default TracingController instance. At present there is
+     * only one TracingController instance for all WebView instances,
+     * however this restriction may be relaxed in the future.
+     *
+     * @return the default TracingController instance
+     */
+    @NonNull
+    public static TracingController getInstance() {
+        return WebViewFactory.getProvider().getTracingController();
+    }
+
+    /**
+     * Starts tracing all webviews. Depeding on the trace mode in traceConfig
+     * specifies how the trace events are recorded.
+     *
+     * For tracing modes {@link TracingConfig#RECORD_UNTIL_FULL},
+     * {@link TracingConfig#RECORD_CONTINUOUSLY} and
+     * {@link TracingConfig#RECORD_UNTIL_FULL_LARGE_BUFFER} the events are recorded
+     * using an internal buffer and flushed to the outputStream when
+     * {@link #stopAndFlush(TracingOutputStream, Handler)} is called.
+     *
+     * @param tracingConfig configuration options to use for tracing
+     * @return false if the system is already tracing, true otherwise.
+     */
+    public abstract boolean start(TracingConfig tracingConfig);
+
+    /**
+     * Stops tracing and discards all tracing data.
+     *
+     * This method is particularly useful in conjunction with the
+     * {@link TracingConfig#RECORD_TO_CONSOLE} tracing mode because tracing data is logged to
+     * console and not sent to an outputStream as with
+     * {@link #stopAndFlush(TracingOutputStream, Handler)}.
+     *
+     * @return false if the system was not tracing at the time of the call, true
+     *         otherwise.
+     */
+    public abstract boolean stop();
+
+    /**
+     * Stops tracing and flushes tracing data to the specifid outputStream.
+     *
+     * Note that if the {@link TracingConfig#RECORD_TO_CONSOLE} tracing mode is used
+     * nothing will be sent to the outputStream and no TracingOuputStream methods will be
+     * called. In that case it is more convenient to just use {@link #stop()} instead.
+     *
+     * @param outputStream the output steam the tracing data will be sent to.
+     * @param handler the {@link android.os.Handler} on which the outputStream callbacks
+     *                will be invoked. If the handler is null the current thread's Looper
+     *                will be used.
+     * @return false if the system was not tracing at the time of the call, true
+     *         otherwise.
+     */
+    public abstract boolean stopAndFlush(TracingOutputStream outputStream,
+            @Nullable Handler handler);
+
+    /** True if the system is tracing */
+    public abstract boolean isTracing();
+
+    // TODO: consider adding getTraceBufferUsage, percentage and approx event count.
+    // TODO: consider adding String getCategories(), for obtaining the actual list
+    // of categories used (given that presets are ints).
+
+}
diff --git a/core/java/android/webkit/TracingFileOutputStream.java b/core/java/android/webkit/TracingFileOutputStream.java
new file mode 100644
index 0000000..8a5fa36
--- /dev/null
+++ b/core/java/android/webkit/TracingFileOutputStream.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.webkit;
+
+import android.annotation.NonNull;
+
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+/**
+ * Simple TracingOutputStream implementation which writes the trace data from
+ * {@link TracingController} to a new file.
+ *
+ */
+public class TracingFileOutputStream implements TracingController.TracingOutputStream {
+
+    private FileOutputStream mFileOutput;
+
+    public TracingFileOutputStream(@NonNull String filename) throws FileNotFoundException {
+        mFileOutput = new FileOutputStream(filename);
+    }
+
+    /**
+     * Writes bytes chunk to the file.
+     */
+    public void write(byte[] chunk) {
+        try {
+            mFileOutput.write(chunk);
+        } catch (IOException e) {
+            onIOException(e);
+        }
+    }
+
+    /**
+     * Closes the file.
+     */
+    public void complete() {
+        try {
+            mFileOutput.close();
+        } catch (IOException e) {
+            onIOException(e);
+        }
+    }
+
+    private void onIOException(IOException e) {
+        throw new RuntimeException(e);
+    }
+}
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index e493739..90cc481 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -122,7 +122,13 @@
     }
 
     /** @hide */
-    @IntDef({LOAD_DEFAULT, LOAD_NORMAL, LOAD_CACHE_ELSE_NETWORK, LOAD_NO_CACHE, LOAD_CACHE_ONLY})
+    @IntDef(prefix = { "LOAD_" }, value = {
+            LOAD_DEFAULT,
+            LOAD_NORMAL,
+            LOAD_CACHE_ELSE_NETWORK,
+            LOAD_NO_CACHE,
+            LOAD_CACHE_ONLY
+    })
     @Retention(RetentionPolicy.SOURCE)
     public @interface CacheMode {}
 
@@ -1415,13 +1421,12 @@
     /**
      * @hide
      */
-    @IntDef(flag = true,
-            value = {
-                    MENU_ITEM_NONE,
-                    MENU_ITEM_SHARE,
-                    MENU_ITEM_WEB_SEARCH,
-                    MENU_ITEM_PROCESS_TEXT
-            })
+    @IntDef(flag = true, prefix = { "MENU_ITEM_" }, value = {
+            MENU_ITEM_NONE,
+            MENU_ITEM_SHARE,
+            MENU_ITEM_WEB_SEARCH,
+            MENU_ITEM_PROCESS_TEXT
+    })
     @Retention(RetentionPolicy.SOURCE)
     @Target({ElementType.PARAMETER, ElementType.METHOD})
     private @interface MenuItemFlags {}
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 6f99254..d39c1e1 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -2249,10 +2249,10 @@
     }
 
     /** @hide */
-    @IntDef({
-        RENDERER_PRIORITY_WAIVED,
-        RENDERER_PRIORITY_BOUND,
-        RENDERER_PRIORITY_IMPORTANT
+    @IntDef(prefix = { "RENDERER_PRIORITY_" }, value = {
+            RENDERER_PRIORITY_WAIVED,
+            RENDERER_PRIORITY_BOUND,
+            RENDERER_PRIORITY_IMPORTANT
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface RendererPriority {}
diff --git a/core/java/android/webkit/WebViewClient.java b/core/java/android/webkit/WebViewClient.java
index 46c3983..d0f9eee 100644
--- a/core/java/android/webkit/WebViewClient.java
+++ b/core/java/android/webkit/WebViewClient.java
@@ -256,11 +256,11 @@
     public static final int ERROR_UNSAFE_RESOURCE = -16;
 
     /** @hide */
-    @IntDef({
-        SAFE_BROWSING_THREAT_UNKNOWN,
-        SAFE_BROWSING_THREAT_MALWARE,
-        SAFE_BROWSING_THREAT_PHISHING,
-        SAFE_BROWSING_THREAT_UNWANTED_SOFTWARE
+    @IntDef(prefix = { "SAFE_BROWSING_THREAT_" }, value = {
+            SAFE_BROWSING_THREAT_UNKNOWN,
+            SAFE_BROWSING_THREAT_MALWARE,
+            SAFE_BROWSING_THREAT_PHISHING,
+            SAFE_BROWSING_THREAT_UNWANTED_SOFTWARE
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface SafeBrowsingThreat {}
diff --git a/core/java/android/webkit/WebViewFactoryProvider.java b/core/java/android/webkit/WebViewFactoryProvider.java
index 4c47abc..3ced6a5 100644
--- a/core/java/android/webkit/WebViewFactoryProvider.java
+++ b/core/java/android/webkit/WebViewFactoryProvider.java
@@ -134,6 +134,14 @@
     TokenBindingService getTokenBindingService();
 
     /**
+     * Gets the TracingController instance for this WebView implementation. The
+     * implementation must return the same instance on subsequent calls.
+     *
+     * @return the TracingController instance
+     */
+    TracingController getTracingController();
+
+    /**
      * Gets the ServiceWorkerController instance for this WebView implementation. The
      * implementation must return the same instance on subsequent calls.
      *
diff --git a/core/java/android/widget/DatePicker.java b/core/java/android/widget/DatePicker.java
index dfb3642..b2b93fa 100644
--- a/core/java/android/widget/DatePicker.java
+++ b/core/java/android/widget/DatePicker.java
@@ -107,7 +107,10 @@
     public static final int MODE_CALENDAR = 2;
 
     /** @hide */
-    @IntDef({MODE_SPINNER, MODE_CALENDAR})
+    @IntDef(prefix = { "MODE_" }, value = {
+            MODE_SPINNER,
+            MODE_CALENDAR
+    })
     @Retention(RetentionPolicy.SOURCE)
     public @interface DatePickerMode {}
 
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index e065dc1..a440398 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -4924,7 +4924,10 @@
     }
 
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef({HANDLE_TYPE_SELECTION_START, HANDLE_TYPE_SELECTION_END})
+    @IntDef(prefix = { "HANDLE_TYPE_" }, value = {
+            HANDLE_TYPE_SELECTION_START,
+            HANDLE_TYPE_SELECTION_END
+    })
     public @interface HandleType {}
     public static final int HANDLE_TYPE_SELECTION_START = 0;
     public static final int HANDLE_TYPE_SELECTION_END = 1;
@@ -6129,7 +6132,11 @@
         }
 
         @Retention(RetentionPolicy.SOURCE)
-        @IntDef({MERGE_EDIT_MODE_FORCE_MERGE, MERGE_EDIT_MODE_NEVER_MERGE, MERGE_EDIT_MODE_NORMAL})
+        @IntDef(prefix = { "MERGE_EDIT_MODE_" }, value = {
+                MERGE_EDIT_MODE_FORCE_MERGE,
+                MERGE_EDIT_MODE_NEVER_MERGE,
+                MERGE_EDIT_MODE_NORMAL
+        })
         private @interface MergeMode {}
         private static final int MERGE_EDIT_MODE_FORCE_MERGE = 0;
         private static final int MERGE_EDIT_MODE_NEVER_MERGE = 1;
diff --git a/core/java/android/widget/GridLayout.java b/core/java/android/widget/GridLayout.java
index cbd1e0a..012b918 100644
--- a/core/java/android/widget/GridLayout.java
+++ b/core/java/android/widget/GridLayout.java
@@ -172,7 +172,10 @@
     // Public constants
 
     /** @hide */
-    @IntDef({HORIZONTAL, VERTICAL})
+    @IntDef(prefix = { "HORIZONTAL", "VERTICAL" }, value = {
+            HORIZONTAL,
+            VERTICAL
+    })
     @Retention(RetentionPolicy.SOURCE)
     public @interface Orientation {}
 
@@ -198,7 +201,10 @@
     public static final int UNDEFINED = Integer.MIN_VALUE;
 
     /** @hide */
-    @IntDef({ALIGN_BOUNDS, ALIGN_MARGINS})
+    @IntDef(prefix = { "ALIGN_" }, value = {
+            ALIGN_BOUNDS,
+            ALIGN_MARGINS
+    })
     @Retention(RetentionPolicy.SOURCE)
     public @interface AlignmentMode {}
 
diff --git a/core/java/android/widget/GridView.java b/core/java/android/widget/GridView.java
index fcb44af..1ec9b2f 100644
--- a/core/java/android/widget/GridView.java
+++ b/core/java/android/widget/GridView.java
@@ -65,7 +65,12 @@
 @RemoteView
 public class GridView extends AbsListView {
     /** @hide */
-    @IntDef({NO_STRETCH, STRETCH_SPACING, STRETCH_COLUMN_WIDTH, STRETCH_SPACING_UNIFORM})
+    @IntDef(prefix = { "NO_STRETCH", "STRETCH_" }, value = {
+            NO_STRETCH,
+            STRETCH_SPACING,
+            STRETCH_COLUMN_WIDTH,
+            STRETCH_SPACING_UNIFORM
+    })
     @Retention(RetentionPolicy.SOURCE)
     public @interface StretchMode {}
 
diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java
index 380bf7a..7ea1f1e 100644
--- a/core/java/android/widget/LinearLayout.java
+++ b/core/java/android/widget/LinearLayout.java
@@ -95,13 +95,12 @@
     public static final int VERTICAL = 1;
 
     /** @hide */
-    @IntDef(flag = true,
-            value = {
-                SHOW_DIVIDER_NONE,
-                SHOW_DIVIDER_BEGINNING,
-                SHOW_DIVIDER_MIDDLE,
-                SHOW_DIVIDER_END
-            })
+    @IntDef(flag = true, prefix = { "SHOW_DIVIDER_" }, value = {
+            SHOW_DIVIDER_NONE,
+            SHOW_DIVIDER_BEGINNING,
+            SHOW_DIVIDER_MIDDLE,
+            SHOW_DIVIDER_END
+    })
     @Retention(RetentionPolicy.SOURCE)
     public @interface DividerMode {}
 
diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java
index 4d3189e..d98b865 100644
--- a/core/java/android/widget/NumberPicker.java
+++ b/core/java/android/widget/NumberPicker.java
@@ -510,7 +510,11 @@
      */
     public interface OnScrollListener {
         /** @hide */
-        @IntDef({SCROLL_STATE_IDLE, SCROLL_STATE_TOUCH_SCROLL, SCROLL_STATE_FLING})
+        @IntDef(prefix = { "SCROLL_STATE_" }, value = {
+                SCROLL_STATE_IDLE,
+                SCROLL_STATE_TOUCH_SCROLL,
+                SCROLL_STATE_FLING
+        })
         @Retention(RetentionPolicy.SOURCE)
         public @interface ScrollState {}
 
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index f8083ba..903d3ca 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -751,7 +751,10 @@
     public static final int AUTO_SIZE_TEXT_TYPE_UNIFORM = 1;
 
     /** @hide */
-    @IntDef({AUTO_SIZE_TEXT_TYPE_NONE, AUTO_SIZE_TEXT_TYPE_UNIFORM})
+    @IntDef(prefix = { "AUTO_SIZE_TEXT_TYPE_" }, value = {
+            AUTO_SIZE_TEXT_TYPE_NONE,
+            AUTO_SIZE_TEXT_TYPE_UNIFORM
+    })
     @Retention(RetentionPolicy.SOURCE)
     public @interface AutoSizeTextType {}
     // Default minimum size for auto-sizing text in scaled pixels.
diff --git a/core/java/android/widget/TimePicker.java b/core/java/android/widget/TimePicker.java
index ae6881e..cfec3f2 100644
--- a/core/java/android/widget/TimePicker.java
+++ b/core/java/android/widget/TimePicker.java
@@ -77,7 +77,10 @@
     public static final int MODE_CLOCK = 2;
 
     /** @hide */
-    @IntDef({MODE_SPINNER, MODE_CLOCK})
+    @IntDef(prefix = { "MODE_" }, value = {
+            MODE_SPINNER,
+            MODE_CLOCK
+    })
     @Retention(RetentionPolicy.SOURCE)
     public @interface TimePickerMode {}
 
diff --git a/core/java/android/widget/Toast.java b/core/java/android/widget/Toast.java
index d807120..edcf209 100644
--- a/core/java/android/widget/Toast.java
+++ b/core/java/android/widget/Toast.java
@@ -71,7 +71,10 @@
     static final boolean localLOGV = false;
 
     /** @hide */
-    @IntDef({LENGTH_SHORT, LENGTH_LONG})
+    @IntDef(prefix = { "LENGTH_" }, value = {
+            LENGTH_SHORT,
+            LENGTH_LONG
+    })
     @Retention(RetentionPolicy.SOURCE)
     public @interface Duration {}
 
diff --git a/packages/SystemUI/res/layout/output_chooser.xml b/packages/SystemUI/res/layout/output_chooser.xml
new file mode 100644
index 0000000..22c3bcf
--- /dev/null
+++ b/packages/SystemUI/res/layout/output_chooser.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2017 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.
+-->
+<!-- extends FrameLayout -->
+<com.android.systemui.volume.OutputChooserLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:sysui="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/output_chooser"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:padding="20dp" >
+
+    <com.android.systemui.qs.AutoSizingList
+        android:id="@android:id/list"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:orientation="vertical"
+        sysui:itemHeight="@dimen/qs_detail_item_height"
+        style="@style/AutoSizingList"/>
+
+    <LinearLayout
+        android:id="@android:id/empty"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_gravity="center"
+        android:gravity="center"
+        android:orientation="vertical">
+
+        <ImageView
+            android:id="@android:id/icon"
+            android:layout_width="56dp"
+            android:layout_height="56dp"
+            android:tint="?android:attr/textColorSecondary" />
+
+        <TextView
+            android:id="@android:id/title"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="20dp"
+            android:textAppearance="@style/TextAppearance.QS.DetailEmpty"/>
+    </LinearLayout>
+</com.android.systemui.volume.OutputChooserLayout>
diff --git a/packages/SystemUI/res/layout/output_chooser_item.xml b/packages/SystemUI/res/layout/output_chooser_item.xml
new file mode 100644
index 0000000..ed7df4b
--- /dev/null
+++ b/packages/SystemUI/res/layout/output_chooser_item.xml
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2017 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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:layout_width="match_parent"
+              android:layout_height="wrap_content"
+              android:minHeight="@dimen/qs_detail_item_height"
+              android:background="@drawable/btn_borderless_rect"
+              android:clickable="true"
+              android:focusable="true"
+              android:gravity="center_vertical"
+              android:orientation="horizontal" >
+
+    <ImageView
+        android:id="@android:id/icon"
+        android:layout_width="@dimen/qs_detail_item_icon_width"
+        android:layout_height="@dimen/qs_detail_item_icon_size"
+        android:layout_marginStart="@dimen/qs_detail_item_icon_marginStart"
+        android:layout_marginEnd="@dimen/qs_detail_item_icon_marginEnd"
+        android:tint="?android:attr/textColorPrimary"/>
+
+    <LinearLayout
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="12dp"
+        android:layout_weight="1"
+        android:orientation="vertical" >
+
+        <TextView
+            android:id="@android:id/title"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:textDirection="locale"
+            android:ellipsize="end"
+            android:textAppearance="@style/TextAppearance.QS.DetailItemPrimary" />
+
+        <TextView
+            android:id="@android:id/summary"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:textDirection="locale"
+            android:layout_marginTop="2dp"
+            android:textAppearance="@style/TextAppearance.QS.DetailItemSecondary" />
+    </LinearLayout>
+
+    <ImageView
+        android:id="@android:id/icon2"
+        style="@style/QSBorderlessButton"
+        android:layout_width="48dp"
+        android:layout_height="48dp"
+        android:layout_marginStart="30dp"
+        android:clickable="true"
+        android:focusable="true"
+        android:scaleType="center"
+        android:contentDescription="@*android:string/media_route_controller_disconnect"
+        android:tint="?android:attr/textColorPrimary" />
+
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml
index bab1e5e..f0d2346 100644
--- a/packages/SystemUI/res/layout/volume_dialog.xml
+++ b/packages/SystemUI/res/layout/volume_dialog.xml
@@ -70,58 +70,77 @@
                 android:layout_height="@dimen/volume_button_size"
                 android:clickable="true"
                 android:soundEffectsEnabled="false"
-                android:src="@drawable/ic_volume_collapse_animation"
+                android:src="@drawable/ic_volume_expand_animation"
                 android:background="@drawable/ripple_drawable"
                 tools:ignore="RtlHardcoded" />
         </LinearLayout>
-        <!-- special row for ringer mode -->
         <RelativeLayout
-            android:id="@+id/ringer_mode"
+            android:id="@+id/footer"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:background="@drawable/rounded_bg_full"
             android:clipChildren="false"
             android:clipToPadding="false"
             android:layout_below="@id/volume_dialog_content"
             android:layout_margin="10dp">
+            <!-- special row for ringer mode -->
+            <RelativeLayout
+                android:id="@+id/ringer_mode"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:background="@drawable/rounded_bg_full"
+                android:clipChildren="false"
+                android:clipToPadding="false"
+                android:layout_toStartOf="@id/output_chooser"
+                android:layout_margin="10dp">
 
+                <com.android.keyguard.AlphaOptimizedImageButton
+                    android:id="@+id/ringer_icon"
+                    style="@style/VolumeButtons"
+                    android:background="?android:selectableItemBackgroundBorderless"
+                    android:layout_width="@dimen/volume_button_size"
+                    android:layout_height="@dimen/volume_button_size"
+                    android:layout_alignParentStart="true"
+                    android:layout_centerVertical="true"
+                    android:soundEffectsEnabled="false" />
+
+                <TextView
+                    android:id="@+id/ringer_title"
+                    android:text="@string/ring_toggle_title"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:ellipsize="end"
+                    android:maxLines="1"
+                    android:layout_alignParentStart="true"
+                    android:layout_centerVertical="true"
+                    android:layout_toEndOf="@+id/ringer_icon"
+                    android:layout_marginStart="64dp"
+                    android:textColor="?android:attr/colorControlNormal"
+                    android:textAppearance="?android:attr/textAppearanceSmall"
+                    android:paddingStart="@dimen/volume_row_header_padding_start" />
+
+                <TextView
+                    android:id="@+id/ringer_status"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:ellipsize="end"
+                    android:layout_alignParentEnd="true"
+                    android:layout_centerVertical="true"
+                    android:layout_marginEnd="14dp"
+                    android:maxLines="1"
+                    android:textColor="?android:attr/colorControlNormal"
+                    android:textAppearance="?android:attr/textAppearanceSmall" />
+
+            </RelativeLayout>
             <com.android.keyguard.AlphaOptimizedImageButton
-                android:id="@+id/ringer_icon"
+                android:id="@+id/output_chooser"
                 style="@style/VolumeButtons"
                 android:background="?android:selectableItemBackgroundBorderless"
                 android:layout_width="@dimen/volume_button_size"
                 android:layout_height="@dimen/volume_button_size"
-                android:layout_alignParentStart="true"
-                android:layout_centerVertical="true"
-                android:soundEffectsEnabled="false" />
-
-            <TextView
-                android:id="@+id/ringer_title"
-                android:text="@string/stream_ring"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:ellipsize="end"
-                android:maxLines="1"
-                android:layout_alignParentStart="true"
-                android:layout_centerVertical="true"
-                android:layout_toEndOf="@+id/ringer_icon"
-                android:layout_marginStart="64dp"
-                android:textColor="?android:attr/colorControlNormal"
-                android:textAppearance="?android:attr/textAppearanceSmall"
-                android:paddingStart="@dimen/volume_row_header_padding_start" />
-
-            <TextView
-                android:id="@+id/ringer_status"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:ellipsize="end"
                 android:layout_alignParentEnd="true"
                 android:layout_centerVertical="true"
-                android:layout_marginEnd="14dp"
-                android:maxLines="1"
-                android:textColor="?android:attr/colorControlNormal"
-                android:textAppearance="?android:attr/textAppearanceSmall" />
-
+                android:src="@drawable/ic_settings_bluetooth"
+                android:soundEffectsEnabled="false" />
         </RelativeLayout>
     </RelativeLayout>
 </com.android.systemui.volume.VolumeUiLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 62ab74d..78e621e 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1248,6 +1248,7 @@
     <string name="stream_tts" translatable="false">Transmitted Through Speaker</string> <!-- STREAM_TTS -->
     <string name="stream_accessibility">Accessibility</string> <!-- STREAM_ACCESSIBILITY -->
 
+    <string name="ring_toggle_title">Calls</string>
     <string name="volume_ringer_status_normal">Ring</string>
     <string name="volume_ringer_status_vibrate">Vibrate</string>
     <string name="volume_ringer_status_silent">Mute</string>
diff --git a/packages/SystemUI/src/com/android/systemui/volume/Events.java b/packages/SystemUI/src/com/android/systemui/volume/Events.java
index 49a12f4..e1376ca 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/Events.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/Events.java
@@ -80,6 +80,7 @@
     public static final int DISMISS_REASON_SETTINGS_CLICKED = 5;
     public static final int DISMISS_REASON_DONE_CLICKED = 6;
     public static final int DISMISS_STREAM_GONE = 7;
+    public static final int DISMISS_REASON_OUTPUT_CHOOSER = 8;
     public static final String[] DISMISS_REASONS = {
             "unknown",
             "touch_outside",
@@ -88,7 +89,8 @@
             "screen_off",
             "settings_clicked",
             "done_clicked",
-            "a11y_stream_changed"
+            "a11y_stream_changed",
+            "output_chooser"
     };
 
     public static final int SHOW_REASON_UNKNOWN = 0;
diff --git a/packages/SystemUI/src/com/android/systemui/volume/OutputChooserDialog.java b/packages/SystemUI/src/com/android/systemui/volume/OutputChooserDialog.java
new file mode 100644
index 0000000..fa82e33
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/OutputChooserDialog.java
@@ -0,0 +1,191 @@
+/*
+ * 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.volume;
+
+import static com.android.settingslib.bluetooth.Utils.getBtClassDrawableWithDescription;
+
+import android.bluetooth.BluetoothClass;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.util.Log;
+import android.util.Pair;
+
+import com.android.settingslib.Utils;
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.systemui.Dependency;
+import com.android.systemui.R;
+import com.android.systemui.statusbar.phone.SystemUIDialog;
+import com.android.systemui.statusbar.policy.BluetoothController;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+public class OutputChooserDialog extends SystemUIDialog
+        implements DialogInterface.OnDismissListener, OutputChooserLayout.Callback {
+
+    private static final String TAG = Util.logTag(OutputChooserDialog.class);
+    private static final int MAX_DEVICES = 10;
+
+    private final Context mContext;
+    private final BluetoothController mController;
+    private OutputChooserLayout mView;
+
+
+    public OutputChooserDialog(Context context) {
+        super(context);
+        mContext = context;
+        mController = Dependency.get(BluetoothController.class);
+
+        final IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
+        context.registerReceiver(mReceiver, filter);
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.output_chooser);
+        setCanceledOnTouchOutside(true);
+        setOnDismissListener(this::onDismiss);
+        mView = findViewById(R.id.output_chooser);
+        mView.setCallback(this);
+        updateItems();
+        mController.addCallback(mCallback);
+    }
+
+    protected void cleanUp() {}
+
+
+    @Override
+    protected void onStart() {
+        super.onStart();
+    }
+
+    @Override
+    public void onDismiss(DialogInterface unused) {
+        mContext.unregisterReceiver(mReceiver);
+        mController.removeCallback(mCallback);
+        cleanUp();
+    }
+
+    @Override
+    public void onDetailItemClick(OutputChooserLayout.Item item) {
+        if (item == null || item.tag == null) return;
+        final CachedBluetoothDevice device = (CachedBluetoothDevice) item.tag;
+        if (device != null && device.getMaxConnectionState()
+                == BluetoothProfile.STATE_DISCONNECTED) {
+            mController.connect(device);
+        }
+    }
+
+    @Override
+    public void onDetailItemDisconnect(OutputChooserLayout.Item item) {
+        if (item == null || item.tag == null) return;
+        final CachedBluetoothDevice device = (CachedBluetoothDevice) item.tag;
+        if (device != null) {
+            mController.disconnect(device);
+        }
+    }
+
+    private void updateItems() {
+        if (mView == null) return;
+        if (mController.isBluetoothEnabled()) {
+            mView.setEmptyState(R.drawable.ic_qs_bluetooth_detail_empty,
+                    R.string.quick_settings_bluetooth_detail_empty_text);
+            mView.setItemsVisible(true);
+        } else {
+            mView.setEmptyState(R.drawable.ic_qs_bluetooth_detail_empty,
+                    R.string.bt_is_off);
+            mView.setItemsVisible(false);
+        }
+        ArrayList<OutputChooserLayout.Item> items = new ArrayList<>();
+        final Collection<CachedBluetoothDevice> devices = mController.getDevices();
+        if (devices != null) {
+            int connectedDevices = 0;
+            int count = 0;
+            for (CachedBluetoothDevice device : devices) {
+                if (mController.getBondState(device) == BluetoothDevice.BOND_NONE) continue;
+                final int majorClass = device.getBtClass().getMajorDeviceClass();
+                if (majorClass != BluetoothClass.Device.Major.AUDIO_VIDEO
+                        && majorClass != BluetoothClass.Device.Major.UNCATEGORIZED) {
+                    continue;
+                }
+                final OutputChooserLayout.Item item = new OutputChooserLayout.Item();
+                item.iconResId = R.drawable.ic_qs_bluetooth_on;
+                item.line1 = device.getName();
+                item.tag = device;
+                int state = device.getMaxConnectionState();
+                if (state == BluetoothProfile.STATE_CONNECTED) {
+                    item.iconResId = R.drawable.ic_qs_bluetooth_connected;
+                    int batteryLevel = device.getBatteryLevel();
+                    if (batteryLevel != BluetoothDevice.BATTERY_LEVEL_UNKNOWN) {
+                        Pair<Drawable, String> pair =
+                                getBtClassDrawableWithDescription(getContext(), device);
+                        item.icon = pair.first;
+                        item.line2 = mContext.getString(
+                                R.string.quick_settings_connected_battery_level,
+                                Utils.formatPercentage(batteryLevel));
+                    } else {
+                        item.line2 = mContext.getString(R.string.quick_settings_connected);
+                    }
+                    item.canDisconnect = true;
+                    items.add(connectedDevices, item);
+                    connectedDevices++;
+                } else if (state == BluetoothProfile.STATE_CONNECTING) {
+                    item.iconResId = R.drawable.ic_qs_bluetooth_connecting;
+                    item.line2 = mContext.getString(R.string.quick_settings_connecting);
+                    items.add(connectedDevices, item);
+                } else {
+                    items.add(item);
+                }
+                if (++count == MAX_DEVICES) {
+                    break;
+                }
+            }
+        }
+        mView.setItems(items.toArray(new OutputChooserLayout.Item[items.size()]));
+    }
+
+    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) {
+                if (D.BUG) Log.d(TAG, "Received ACTION_CLOSE_SYSTEM_DIALOGS");
+                cancel();
+                cleanUp();
+            }
+        }
+    };
+
+    private final BluetoothController.Callback mCallback = new BluetoothController.Callback() {
+        @Override
+        public void onBluetoothStateChange(boolean enabled) {
+            updateItems();
+        }
+
+        @Override
+        public void onBluetoothDevicesChanged() {
+            updateItems();
+        }
+    };
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/volume/OutputChooserLayout.java b/packages/SystemUI/src/com/android/systemui/volume/OutputChooserLayout.java
new file mode 100644
index 0000000..e8be4fd
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/OutputChooserLayout.java
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) 2017 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.volume;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.android.systemui.FontSizeUtils;
+import com.android.systemui.R;
+import com.android.systemui.qs.AutoSizingList;
+
+/**
+ * Limited height list of devices.
+ */
+public class OutputChooserLayout extends FrameLayout {
+    private static final String TAG = "OutputChooserLayout";
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+    private final int mQsDetailIconOverlaySize;
+    private final Context mContext;
+    private final H mHandler = new H();
+    private final Adapter mAdapter = new Adapter();
+
+    private String mTag;
+    private Callback mCallback;
+    private boolean mItemsVisible = true;
+    private AutoSizingList mItemList;
+    private View mEmpty;
+    private TextView mEmptyText;
+    private ImageView mEmptyIcon;
+
+    private Item[] mItems;
+
+    public OutputChooserLayout(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        mContext = context;
+        mTag = TAG;
+        mQsDetailIconOverlaySize = (int) getResources().getDimension(
+                R.dimen.qs_detail_icon_overlay_size);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mItemList = findViewById(android.R.id.list);
+        mItemList.setVisibility(GONE);
+        mItemList.setAdapter(mAdapter);
+        mEmpty = findViewById(android.R.id.empty);
+        mEmpty.setVisibility(GONE);
+        mEmptyText = mEmpty.findViewById(android.R.id.title);
+        mEmptyIcon = mEmpty.findViewById(android.R.id.icon);
+    }
+
+    @Override
+    protected void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+        FontSizeUtils.updateFontSize(mEmptyText, R.dimen.qs_detail_empty_text_size);
+        int count = mItemList.getChildCount();
+        for (int i = 0; i < count; i++) {
+            View item = mItemList.getChildAt(i);
+            FontSizeUtils.updateFontSize(item, android.R.id.title,
+                    R.dimen.qs_detail_item_primary_text_size);
+            FontSizeUtils.updateFontSize(item, android.R.id.summary,
+                    R.dimen.qs_detail_item_secondary_text_size);
+        }
+    }
+
+    public void setEmptyState(int icon, int text) {
+        mEmpty.post(() -> {
+            mEmptyIcon.setImageResource(icon);
+            mEmptyText.setText(text);
+        });
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        if (DEBUG) Log.d(mTag, "onAttachedToWindow");
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        if (DEBUG) Log.d(mTag, "onDetachedFromWindow");
+        mCallback = null;
+    }
+
+    public void setCallback(Callback callback) {
+        mHandler.removeMessages(H.SET_CALLBACK);
+        mHandler.obtainMessage(H.SET_CALLBACK, callback).sendToTarget();
+    }
+
+    public void setItems(Item[] items) {
+        mHandler.removeMessages(H.SET_ITEMS);
+        mHandler.obtainMessage(H.SET_ITEMS, items).sendToTarget();
+    }
+
+    public void setItemsVisible(boolean visible) {
+        mHandler.removeMessages(H.SET_ITEMS_VISIBLE);
+        mHandler.obtainMessage(H.SET_ITEMS_VISIBLE, visible ? 1 : 0, 0).sendToTarget();
+    }
+
+    private void handleSetCallback(Callback callback) {
+        mCallback = callback;
+    }
+
+    private void handleSetItems(Item[] items) {
+        final int itemCount = items != null ? items.length : 0;
+        mEmpty.setVisibility(itemCount == 0 ? VISIBLE : GONE);
+        mItemList.setVisibility(itemCount == 0 ? GONE : VISIBLE);
+        mItems = items;
+        mAdapter.notifyDataSetChanged();
+    }
+
+    private void handleSetItemsVisible(boolean visible) {
+        if (mItemsVisible == visible) return;
+        mItemsVisible = visible;
+        for (int i = 0; i < mItemList.getChildCount(); i++) {
+            mItemList.getChildAt(i).setVisibility(mItemsVisible ? VISIBLE : INVISIBLE);
+        }
+    }
+
+    private class Adapter extends BaseAdapter {
+
+        @Override
+        public int getCount() {
+            return mItems != null ? mItems.length : 0;
+        }
+
+        @Override
+        public Object getItem(int position) {
+            return mItems[position];
+        }
+
+        @Override
+        public long getItemId(int position) {
+            return 0;
+        }
+
+        @Override
+        public View getView(int position, View view, ViewGroup parent) {
+            final Item item = mItems[position];
+            if (view == null) {
+                view = LayoutInflater.from(mContext).inflate(R.layout.output_chooser_item, parent,
+                        false);
+            }
+            view.setVisibility(mItemsVisible ? VISIBLE : INVISIBLE);
+            final ImageView iv = view.findViewById(android.R.id.icon);
+            if (item.icon != null) {
+                iv.setImageDrawable(item.icon);
+            } else {
+                iv.setImageResource(item.iconResId);
+            }
+            iv.getOverlay().clear();
+            if (item.overlay != null) {
+                item.overlay.setBounds(0, 0, mQsDetailIconOverlaySize, mQsDetailIconOverlaySize);
+                iv.getOverlay().add(item.overlay);
+            }
+            final TextView title = view.findViewById(android.R.id.title);
+            title.setText(item.line1);
+            final TextView summary =  view.findViewById(android.R.id.summary);
+            final boolean twoLines = !TextUtils.isEmpty(item.line2);
+            title.setMaxLines(twoLines ? 1 : 2);
+            summary.setVisibility(twoLines ? VISIBLE : GONE);
+            summary.setText(twoLines ? item.line2 : null);
+            view.setOnClickListener(v -> {
+                if (mCallback != null) {
+                    mCallback.onDetailItemClick(item);
+                }
+            });
+
+            final ImageView icon2 = view.findViewById(android.R.id.icon2);
+            if (item.canDisconnect) {
+                icon2.setImageResource(R.drawable.ic_qs_cancel);
+                icon2.setVisibility(VISIBLE);
+                icon2.setClickable(true);
+                icon2.setOnClickListener(v -> {
+                    if (mCallback != null) {
+                        mCallback.onDetailItemDisconnect(item);
+                    }
+                });
+            } else if (item.icon2 != -1) {
+                icon2.setVisibility(VISIBLE);
+                icon2.setImageResource(item.icon2);
+                icon2.setClickable(false);
+            } else {
+                icon2.setVisibility(GONE);
+            }
+
+            return view;
+        }
+    };
+
+    private class H extends Handler {
+        private static final int SET_ITEMS = 1;
+        private static final int SET_CALLBACK = 2;
+        private static final int SET_ITEMS_VISIBLE = 3;
+
+        public H() {
+            super(Looper.getMainLooper());
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            if (msg.what == SET_ITEMS) {
+                handleSetItems((Item[]) msg.obj);
+            } else if (msg.what == SET_CALLBACK) {
+                handleSetCallback((OutputChooserLayout.Callback) msg.obj);
+            } else if (msg.what == SET_ITEMS_VISIBLE) {
+                handleSetItemsVisible(msg.arg1 != 0);
+            }
+        }
+    }
+
+    public static class Item {
+        public int iconResId;
+        public Drawable icon;
+        public Drawable overlay;
+        public CharSequence line1;
+        public CharSequence line2;
+        public Object tag;
+        public boolean canDisconnect;
+        public int icon2 = -1;
+    }
+
+    public interface Callback {
+        void onDetailItemClick(Item item);
+        void onDetailItemDisconnect(Item item);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 7959b72..d7c8010 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -19,6 +19,7 @@
 import static android.accessibilityservice.AccessibilityServiceInfo.FEEDBACK_ALL_MASK;
 import static android.accessibilityservice.AccessibilityServiceInfo.FEEDBACK_GENERIC;
 
+import static com.android.systemui.volume.Events.DISMISS_REASON_OUTPUT_CHOOSER;
 import static com.android.systemui.volume.Events.DISMISS_REASON_TOUCH_OUTSIDE;
 
 import android.accessibilityservice.AccessibilityServiceInfo;
@@ -37,7 +38,6 @@
 import android.graphics.drawable.AnimatedVectorDrawable;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
-import android.graphics.drawable.TransitionDrawable;
 import android.media.AudioManager;
 import android.media.AudioSystem;
 import android.os.Debug;
@@ -106,6 +106,7 @@
     private ViewGroup mDialogRowsView;
     private ImageButton mExpandButton;
     private ImageButton mRingerIcon;
+    private ImageButton mOutputChooser;
     private TextView mRingerStatus;
     private final List<VolumeRow> mRows = new ArrayList<>();
     private ConfigurableTexts mConfigurableTexts;
@@ -113,6 +114,7 @@
     private final KeyguardManager mKeyguard;
     private final AccessibilityManager mAccessibilityMgr;
     private final Object mSafetyWarningLock = new Object();
+    private final Object mOutputChooserLock = new Object();
     private final Accessibility mAccessibility = new Accessibility();
     private final ColorStateList mActiveSliderTint;
     private final ColorStateList mInactiveSliderTint;
@@ -128,6 +130,7 @@
     private boolean mSilentMode = VolumePrefs.DEFAULT_ENABLE_SILENT_MODE;
     private State mState;
     private SafetyWarningDialog mSafetyWarning;
+    private OutputChooserDialog mOutputChooserDialog;
     private boolean mHovering = false;
 
     public VolumeDialogImpl(Context context) {
@@ -213,6 +216,9 @@
         mExpandButton.setVisibility(
                 AudioSystem.isSingleVolume(mContext) ? View.GONE : View.VISIBLE);
 
+        mOutputChooser = mDialogView.findViewById(R.id.output_chooser);
+        mOutputChooser.setOnClickListener(mClickOutputChooser);
+
         if (mRows.isEmpty()) {
             addRow(AudioManager.STREAM_MUSIC,
                     R.drawable.ic_volume_media, R.drawable.ic_volume_media_mute, true, true);
@@ -913,6 +919,23 @@
         rescheduleTimeoutH();
     }
 
+    private void showOutputChooserH() {
+        synchronized (mOutputChooserLock) {
+            if (mOutputChooserDialog != null) {
+                return;
+            }
+            mOutputChooserDialog = new OutputChooserDialog(mContext) {
+                @Override
+                protected void cleanUp() {
+                    synchronized (mOutputChooserLock) {
+                        mOutputChooserDialog = null;
+                    }
+                }
+            };
+            mOutputChooserDialog.show();
+        }
+    }
+
     private String getStreamLabelH(StreamState ss) {
         if (ss.remoteLabel != null) {
             return ss.remoteLabel;
@@ -935,6 +958,15 @@
         }
     };
 
+    private final OnClickListener mClickOutputChooser = new OnClickListener() {
+        @Override
+        public void onClick(View v) {
+            // TODO: log
+            dismissH(DISMISS_REASON_OUTPUT_CHOOSER);
+            showOutputChooserH();
+        }
+    };
+
     private final VolumeDialogController.Callbacks mControllerCallbackH
             = new VolumeDialogController.Callbacks() {
         @Override
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index ac0cdd7..5ec3850 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -28,6 +28,7 @@
 import android.annotation.Nullable;
 import android.app.ActivityManagerInternal;
 import android.app.AlertDialog;
+import android.app.AppOpsManager;
 import android.app.PendingIntent;
 import android.appwidget.AppWidgetManagerInternal;
 import android.content.BroadcastReceiver;
@@ -193,6 +194,8 @@
 
     private final SecurityPolicy mSecurityPolicy;
 
+    private final AppOpsManager mAppOpsManager;
+
     private final MainHandler mMainHandler;
 
     private final GlobalActionPerformer mGlobalActionPerformer;
@@ -261,6 +264,7 @@
         mWindowManagerService = LocalServices.getService(WindowManagerInternal.class);
         mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
         mSecurityPolicy = new SecurityPolicy();
+        mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
         mMainHandler = new MainHandler(mContext.getMainLooper());
         mGlobalActionPerformer = new GlobalActionPerformer(mContext, mWindowManagerService);
 
@@ -1223,14 +1227,11 @@
         for (int i = 0, count = installedServices.size(); i < count; i++) {
             ResolveInfo resolveInfo = installedServices.get(i);
             ServiceInfo serviceInfo = resolveInfo.serviceInfo;
-            if (!android.Manifest.permission.BIND_ACCESSIBILITY_SERVICE.equals(
-                    serviceInfo.permission)) {
-                Slog.w(LOG_TAG, "Skipping accessibilty service " + new ComponentName(
-                        serviceInfo.packageName, serviceInfo.name).flattenToShortString()
-                        + ": it does not require the permission "
-                        + android.Manifest.permission.BIND_ACCESSIBILITY_SERVICE);
+
+            if (!canRegisterService(serviceInfo)) {
                 continue;
             }
+
             AccessibilityServiceInfo accessibilityServiceInfo;
             try {
                 accessibilityServiceInfo = new AccessibilityServiceInfo(resolveInfo, mContext);
@@ -1251,6 +1252,28 @@
         return false;
     }
 
+    private boolean canRegisterService(ServiceInfo serviceInfo) {
+        if (!android.Manifest.permission.BIND_ACCESSIBILITY_SERVICE.equals(
+                serviceInfo.permission)) {
+            Slog.w(LOG_TAG, "Skipping accessibility service " + new ComponentName(
+                    serviceInfo.packageName, serviceInfo.name).flattenToShortString()
+                    + ": it does not require the permission "
+                    + android.Manifest.permission.BIND_ACCESSIBILITY_SERVICE);
+            return false;
+        }
+
+        int servicePackageUid = serviceInfo.applicationInfo.uid;
+        if (mAppOpsManager.noteOpNoThrow(AppOpsManager.OP_BIND_ACCESSIBILITY_SERVICE,
+                servicePackageUid, serviceInfo.packageName) != AppOpsManager.MODE_ALLOWED) {
+            Slog.w(LOG_TAG, "Skipping accessibility service " + new ComponentName(
+                    serviceInfo.packageName, serviceInfo.name).flattenToShortString()
+                    + ": disallowed by AppOps");
+            return false;
+        }
+
+        return true;
+    }
+
     private boolean readEnabledAccessibilityServicesLocked(UserState userState) {
         mTempComponentNameSet.clear();
         readComponentNamesFromSettingLocked(Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
diff --git a/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java b/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java
index 1dba052..4adcb99 100644
--- a/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java
@@ -3242,10 +3242,10 @@
             skip = true;
         }
 
-        // Do we have a transport to fetch data for us?
-        IBackupTransport transport = mTransportManager.getCurrentTransportBinder();
-        if (transport == null) {
-            if (DEBUG) Slog.w(TAG, "No transport");
+        TransportClient transportClient =
+                mTransportManager.getCurrentTransportClient("BMS.restoreAtInstall()");
+        if (transportClient == null) {
+            if (DEBUG) Slog.w(TAG, "No transport client");
             skip = true;
         }
 
@@ -3262,16 +3262,26 @@
                 // The eventual message back into the Package Manager to run the post-install
                 // steps for 'token' will be issued from the restore handling code.
 
-                // This can throw and so *must* happen before the wakelock is acquired
-                String dirName = transport.transportDirName();
-
                 mWakelock.acquire();
+
+                OnTaskFinishedListener listener = caller -> {
+                        mTransportManager.disposeOfTransportClient(transportClient, caller);
+                        mWakelock.release();
+                };
+
                 if (MORE_DEBUG) {
                     Slog.d(TAG, "Restore at install of " + packageName);
                 }
                 Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
-                msg.obj = new RestoreParams(transport, dirName, null, null,
-                        restoreSet, packageName, token);
+                msg.obj =
+                        RestoreParams.createForRestoreAtInstall(
+                                transportClient,
+                                /* observer */ null,
+                                /* monitor */ null,
+                                restoreSet,
+                                packageName,
+                                token,
+                                listener);
                 mBackupHandler.sendMessage(msg);
             } catch (Exception e) {
                 // Calling into the transport broke; back off and proceed with the installation.
@@ -3281,8 +3291,14 @@
         }
 
         if (skip) {
-            // Auto-restore disabled or no way to attempt a restore; just tell the Package
-            // Manager to proceed with the post-install handling for this package.
+            // Auto-restore disabled or no way to attempt a restore
+
+            if (transportClient != null) {
+                mTransportManager.disposeOfTransportClient(
+                        transportClient, "BMS.restoreAtInstall()");
+            }
+
+            // Tell the PackageManager to proceed with the post-install handling for this package.
             if (DEBUG) Slog.v(TAG, "Finishing install immediately");
             try {
                 mPackageManagerBinder.finishPackageInstall(token, false);
diff --git a/services/backup/java/com/android/server/backup/TransportManager.java b/services/backup/java/com/android/server/backup/TransportManager.java
index f8f1448..1f3ebf9 100644
--- a/services/backup/java/com/android/server/backup/TransportManager.java
+++ b/services/backup/java/com/android/server/backup/TransportManager.java
@@ -308,6 +308,12 @@
         }
     }
 
+    public boolean isTransportRegistered(String transportName) {
+        synchronized (mTransportLock) {
+            return getRegisteredTransportEntryLocked(transportName) != null;
+        }
+    }
+
     /**
      * Returns a {@link TransportClient} for the current transport or null if not found.
      *
diff --git a/services/backup/java/com/android/server/backup/internal/BackupHandler.java b/services/backup/java/com/android/server/backup/internal/BackupHandler.java
index 477724d..4c78348 100644
--- a/services/backup/java/com/android/server/backup/internal/BackupHandler.java
+++ b/services/backup/java/com/android/server/backup/internal/BackupHandler.java
@@ -189,7 +189,7 @@
                     }
                     task.execute();
                 } catch (ClassCastException e) {
-                    Slog.e(TAG, "Invalid backup task in flight, obj=" + msg.obj);
+                    Slog.e(TAG, "Invalid backup/restore task in flight, obj=" + msg.obj);
                 }
                 break;
             }
@@ -229,10 +229,18 @@
                 RestoreParams params = (RestoreParams) msg.obj;
                 Slog.d(TAG, "MSG_RUN_RESTORE observer=" + params.observer);
 
-                PerformUnifiedRestoreTask task = new PerformUnifiedRestoreTask(backupManagerService,
-                        params.transport,
-                        params.observer, params.monitor, params.token, params.pkgInfo,
-                        params.pmToken, params.isSystemRestore, params.filterSet);
+                PerformUnifiedRestoreTask task =
+                        new PerformUnifiedRestoreTask(
+                                backupManagerService,
+                                params.transportClient,
+                                params.observer,
+                                params.monitor,
+                                params.token,
+                                params.packageInfo,
+                                params.pmToken,
+                                params.isSystemRestore,
+                                params.filterSet,
+                                params.listener);
 
                 synchronized (backupManagerService.getPendingRestores()) {
                     if (backupManagerService.isRestoreInProgress()) {
@@ -299,8 +307,11 @@
                 // Like other async operations, this is entered with the wakelock held
                 RestoreSet[] sets = null;
                 RestoreGetSetsParams params = (RestoreGetSetsParams) msg.obj;
+                String callerLogString = "BH/MSG_RUN_GET_RESTORE_SETS";
                 try {
-                    sets = params.transport.getAvailableRestoreSets();
+                    IBackupTransport transport =
+                            params.transportClient.connectOrThrow(callerLogString);
+                    sets = transport.getAvailableRestoreSets();
                     // cache the result in the active session
                     synchronized (params.session) {
                         params.session.mRestoreSets = sets;
@@ -325,7 +336,7 @@
                     removeMessages(MSG_RESTORE_SESSION_TIMEOUT);
                     sendEmptyMessageDelayed(MSG_RESTORE_SESSION_TIMEOUT, TIMEOUT_RESTORE_INTERVAL);
 
-                    backupManagerService.getWakelock().release();
+                    params.listener.onFinished(callerLogString);
                 }
                 break;
             }
diff --git a/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java b/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java
index e65eb28..a002334 100644
--- a/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java
+++ b/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java
@@ -907,7 +907,7 @@
             mStatus = BackupTransport.TRANSPORT_OK;
             long size = 0;
             try {
-                TransportUtils.checkTransport(transport);
+                TransportUtils.checkTransportNotNull(transport);
                 size = mBackupDataName.length();
                 if (size > 0) {
                     if (mStatus == BackupTransport.TRANSPORT_OK) {
@@ -997,7 +997,7 @@
                 }
                 if (mAgentBinder != null) {
                     try {
-                        TransportUtils.checkTransport(transport);
+                        TransportUtils.checkTransportNotNull(transport);
                         long quota = transport.getBackupQuota(mCurrentPackage.packageName, false);
                         mAgentBinder.doQuotaExceeded(size, quota);
                     } catch (Exception e) {
diff --git a/services/backup/java/com/android/server/backup/params/RestoreGetSetsParams.java b/services/backup/java/com/android/server/backup/params/RestoreGetSetsParams.java
index bff476b..914e9ea 100644
--- a/services/backup/java/com/android/server/backup/params/RestoreGetSetsParams.java
+++ b/services/backup/java/com/android/server/backup/params/RestoreGetSetsParams.java
@@ -20,20 +20,24 @@
 import android.app.backup.IRestoreObserver;
 
 import com.android.internal.backup.IBackupTransport;
+import com.android.server.backup.internal.OnTaskFinishedListener;
 import com.android.server.backup.restore.ActiveRestoreSession;
+import com.android.server.backup.transport.TransportClient;
 
 public class RestoreGetSetsParams {
+    public final TransportClient transportClient;
+    public final ActiveRestoreSession session;
+    public final IRestoreObserver observer;
+    public final IBackupManagerMonitor monitor;
+    public final OnTaskFinishedListener listener;
 
-    public IBackupTransport transport;
-    public ActiveRestoreSession session;
-    public IRestoreObserver observer;
-    public IBackupManagerMonitor monitor;
-
-    public RestoreGetSetsParams(IBackupTransport _transport, ActiveRestoreSession _session,
-            IRestoreObserver _observer, IBackupManagerMonitor _monitor) {
-        transport = _transport;
+    public RestoreGetSetsParams(TransportClient _transportClient, ActiveRestoreSession _session,
+            IRestoreObserver _observer, IBackupManagerMonitor _monitor,
+            OnTaskFinishedListener _listener) {
+        transportClient = _transportClient;
         session = _session;
         observer = _observer;
         monitor = _monitor;
+        listener = _listener;
     }
 }
diff --git a/services/backup/java/com/android/server/backup/params/RestoreParams.java b/services/backup/java/com/android/server/backup/params/RestoreParams.java
index 93ce00d..e500d6e 100644
--- a/services/backup/java/com/android/server/backup/params/RestoreParams.java
+++ b/services/backup/java/com/android/server/backup/params/RestoreParams.java
@@ -20,84 +20,128 @@
 import android.app.backup.IRestoreObserver;
 import android.content.pm.PackageInfo;
 
-import com.android.internal.backup.IBackupTransport;
+import com.android.server.backup.internal.OnTaskFinishedListener;
+import com.android.server.backup.transport.TransportClient;
 
 public class RestoreParams {
-
-    public IBackupTransport transport;
-    public String dirName;
-    public IRestoreObserver observer;
-    public IBackupManagerMonitor monitor;
-    public long token;
-    public PackageInfo pkgInfo;
-    public int pmToken; // in post-install restore, the PM's token for this transaction
-    public boolean isSystemRestore;
-    public String[] filterSet;
+    public final TransportClient transportClient;
+    public final IRestoreObserver observer;
+    public final IBackupManagerMonitor monitor;
+    public final long token;
+    public final PackageInfo packageInfo;
+    public final int pmToken; // in post-install restore, the PM's token for this transaction
+    public final boolean isSystemRestore;
+    public final String[] filterSet;
+    public final OnTaskFinishedListener listener;
 
     /**
-     * Restore a single package; no kill after restore
+     * No kill after restore.
      */
-    public RestoreParams(IBackupTransport _transport, String _dirName, IRestoreObserver _obs,
-            IBackupManagerMonitor _monitor, long _token, PackageInfo _pkg) {
-        transport = _transport;
-        dirName = _dirName;
-        observer = _obs;
-        monitor = _monitor;
-        token = _token;
-        pkgInfo = _pkg;
-        pmToken = 0;
-        isSystemRestore = false;
-        filterSet = null;
+    public static RestoreParams createForSinglePackage(
+            TransportClient transportClient,
+            IRestoreObserver observer,
+            IBackupManagerMonitor monitor,
+            long token,
+            PackageInfo packageInfo,
+            OnTaskFinishedListener listener) {
+        return new RestoreParams(
+                transportClient,
+                observer,
+                monitor,
+                token,
+                packageInfo,
+                /* pmToken */ 0,
+                /* isSystemRestore */ false,
+                /* filterSet */ null,
+                listener);
     }
 
     /**
-     * Restore at install: PM token needed, kill after restore
+     * Kill after restore.
      */
-    public RestoreParams(IBackupTransport _transport, String _dirName, IRestoreObserver _obs,
-            IBackupManagerMonitor _monitor, long _token, String _pkgName, int _pmToken) {
-        transport = _transport;
-        dirName = _dirName;
-        observer = _obs;
-        monitor = _monitor;
-        token = _token;
-        pkgInfo = null;
-        pmToken = _pmToken;
-        isSystemRestore = false;
-        filterSet = new String[]{_pkgName};
+    public static RestoreParams createForRestoreAtInstall(
+            TransportClient transportClient,
+            IRestoreObserver observer,
+            IBackupManagerMonitor monitor,
+            long token,
+            String packageName,
+            int pmToken,
+            OnTaskFinishedListener listener) {
+        String[] filterSet = {packageName};
+        return new RestoreParams(
+                transportClient,
+                observer,
+                monitor,
+                token,
+                /* packageInfo */ null,
+                pmToken,
+                /* isSystemRestore */ false,
+                filterSet,
+                listener);
     }
 
     /**
-     * Restore everything possible.  This is the form that Setup Wizard or similar
-     * restore UXes use.
+     * This is the form that Setup Wizard or similar restore UXes use.
      */
-    public RestoreParams(IBackupTransport _transport, String _dirName, IRestoreObserver _obs,
-            IBackupManagerMonitor _monitor, long _token) {
-        transport = _transport;
-        dirName = _dirName;
-        observer = _obs;
-        monitor = _monitor;
-        token = _token;
-        pkgInfo = null;
-        pmToken = 0;
-        isSystemRestore = true;
-        filterSet = null;
+    public static RestoreParams createForRestoreAll(
+            TransportClient transportClient,
+            IRestoreObserver observer,
+            IBackupManagerMonitor monitor,
+            long token,
+            OnTaskFinishedListener listener) {
+        return new RestoreParams(
+                transportClient,
+                observer,
+                monitor,
+                token,
+                /* packageInfo */ null,
+                /* pmToken */ 0,
+                /* isSystemRestore */ true,
+                /* filterSet */ null,
+                listener);
     }
 
     /**
-     * Restore some set of packages.  Leave this one up to the caller to specify
-     * whether it's to be considered a system-level restore.
+     * Caller specifies whether is considered a system-level restore.
      */
-    public RestoreParams(IBackupTransport _transport, String _dirName, IRestoreObserver _obs,
-            IBackupManagerMonitor _monitor, long _token,
-            String[] _filterSet, boolean _isSystemRestore) {
-        transport = _transport;
-        dirName = _dirName;
-        observer = _obs;
-        monitor = _monitor;
-        token = _token;
-        pkgInfo = null;
-        pmToken = 0;
-        isSystemRestore = _isSystemRestore;
-        filterSet = _filterSet;
+    public static RestoreParams createForRestoreSome(
+            TransportClient transportClient,
+            IRestoreObserver observer,
+            IBackupManagerMonitor monitor,
+            long token,
+            String[] filterSet,
+            boolean isSystemRestore,
+            OnTaskFinishedListener listener) {
+        return new RestoreParams(
+                transportClient,
+                observer,
+                monitor,
+                token,
+                /* packageInfo */ null,
+                /* pmToken */ 0,
+                isSystemRestore,
+                filterSet,
+                listener);
+    }
+
+    private RestoreParams(
+            TransportClient transportClient,
+            IRestoreObserver observer,
+            IBackupManagerMonitor monitor,
+            long token,
+            PackageInfo packageInfo,
+            int pmToken,
+            boolean isSystemRestore,
+            String[] filterSet,
+            OnTaskFinishedListener listener) {
+        this.transportClient = transportClient;
+        this.observer = observer;
+        this.monitor = monitor;
+        this.token = token;
+        this.packageInfo = packageInfo;
+        this.pmToken = pmToken;
+        this.isSystemRestore = isSystemRestore;
+        this.filterSet = filterSet;
+        this.listener = listener;
     }
 }
diff --git a/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java b/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java
index a08c19e..7ae5b43 100644
--- a/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java
+++ b/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java
@@ -31,33 +31,39 @@
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.os.Binder;
 import android.os.Message;
+import android.os.PowerManager;
 import android.util.Slog;
 
-import com.android.internal.backup.IBackupTransport;
 import com.android.server.backup.RefactoredBackupManagerService;
+import com.android.server.backup.TransportManager;
+import com.android.server.backup.internal.BackupHandler;
+import com.android.server.backup.internal.OnTaskFinishedListener;
 import com.android.server.backup.params.RestoreGetSetsParams;
 import com.android.server.backup.params.RestoreParams;
+import com.android.server.backup.transport.TransportClient;
+
+import java.util.function.BiFunction;
 
 /**
  * Restore session.
  */
 public class ActiveRestoreSession extends IRestoreSession.Stub {
-
     private static final String TAG = "RestoreSession";
 
-    private RefactoredBackupManagerService backupManagerService;
-    private String mPackageName;
-    private IBackupTransport mRestoreTransport = null;
+    private final TransportManager mTransportManager;
+    private final String mTransportName;
+    private final RefactoredBackupManagerService mBackupManagerService;
+    private final String mPackageName;
     public RestoreSet[] mRestoreSets = null;
     boolean mEnded = false;
     boolean mTimedOut = false;
 
     public ActiveRestoreSession(RefactoredBackupManagerService backupManagerService,
-            String packageName, String transport) {
-        this.backupManagerService = backupManagerService;
+            String packageName, String transportName) {
+        mBackupManagerService = backupManagerService;
         mPackageName = packageName;
-        mRestoreTransport = backupManagerService.getTransportManager().getTransportBinder(
-                transport);
+        mTransportManager = backupManagerService.getTransportManager();
+        mTransportName = transportName;
     }
 
     public void markTimedOut() {
@@ -67,7 +73,7 @@
     // --- Binder interface ---
     public synchronized int getAvailableRestoreSets(IRestoreObserver observer,
             IBackupManagerMonitor monitor) {
-        backupManagerService.getContext().enforceCallingOrSelfPermission(
+        mBackupManagerService.getContext().enforceCallingOrSelfPermission(
                 android.Manifest.permission.BACKUP,
                 "getAvailableRestoreSets");
         if (observer == null) {
@@ -85,23 +91,32 @@
 
         long oldId = Binder.clearCallingIdentity();
         try {
-            if (mRestoreTransport == null) {
-                Slog.w(TAG, "Null transport getting restore sets");
+            TransportClient transportClient =
+                    mTransportManager.getTransportClient(
+                                    mTransportName, "RestoreSession.getAvailableRestoreSets()");
+            if (transportClient == null) {
+                Slog.w(TAG, "Null transport client getting restore sets");
                 return -1;
             }
 
             // We know we're doing legit work now, so halt the timeout
             // until we're done.  It gets started again when the result
             // comes in.
-            backupManagerService.getBackupHandler().removeMessages(MSG_RESTORE_SESSION_TIMEOUT);
+            mBackupManagerService.getBackupHandler().removeMessages(MSG_RESTORE_SESSION_TIMEOUT);
 
-            // spin off the transport request to our service thread
-            backupManagerService.getWakelock().acquire();
-            Message msg = backupManagerService.getBackupHandler().obtainMessage(
+            PowerManager.WakeLock wakelock = mBackupManagerService.getWakelock();
+            wakelock.acquire();
+
+            // Prevent lambda from leaking 'this'
+            TransportManager transportManager = mTransportManager;
+            OnTaskFinishedListener listener = caller -> {
+                    transportManager.disposeOfTransportClient(transportClient, caller);
+                    wakelock.release();
+            };
+            Message msg = mBackupManagerService.getBackupHandler().obtainMessage(
                     MSG_RUN_GET_RESTORE_SETS,
-                    new RestoreGetSetsParams(mRestoreTransport, this, observer,
-                            monitor));
-            backupManagerService.getBackupHandler().sendMessage(msg);
+                    new RestoreGetSetsParams(transportClient, this, observer, monitor, listener));
+            mBackupManagerService.getBackupHandler().sendMessage(msg);
             return 0;
         } catch (Exception e) {
             Slog.e(TAG, "Error in getAvailableRestoreSets", e);
@@ -113,7 +128,7 @@
 
     public synchronized int restoreAll(long token, IRestoreObserver observer,
             IBackupManagerMonitor monitor) {
-        backupManagerService.getContext().enforceCallingOrSelfPermission(
+        mBackupManagerService.getContext().enforceCallingOrSelfPermission(
                 android.Manifest.permission.BACKUP,
                 "performRestore");
 
@@ -131,7 +146,7 @@
             return -1;
         }
 
-        if (mRestoreTransport == null || mRestoreSets == null) {
+        if (mRestoreSets == null) {
             Slog.e(TAG, "Ignoring restoreAll() with no restore set");
             return -1;
         }
@@ -141,37 +156,28 @@
             return -1;
         }
 
-        String dirName;
-        try {
-            dirName = mRestoreTransport.transportDirName();
-        } catch (Exception e) {
-            // Transport went AWOL; fail.
-            Slog.e(TAG, "Unable to get transport dir for restore: " + e.getMessage());
+        if (!mTransportManager.isTransportRegistered(mTransportName)) {
+            Slog.e(TAG, "Transport " + mTransportName + " not registered");
             return -1;
         }
 
-        synchronized (backupManagerService.getQueueLock()) {
+        synchronized (mBackupManagerService.getQueueLock()) {
             for (int i = 0; i < mRestoreSets.length; i++) {
                 if (token == mRestoreSets[i].token) {
-                    // Real work, so stop the session timeout until we finalize the restore
-                    backupManagerService.getBackupHandler().removeMessages(
-                            MSG_RESTORE_SESSION_TIMEOUT);
-
                     long oldId = Binder.clearCallingIdentity();
                     try {
-                        backupManagerService.getWakelock().acquire();
-                        if (MORE_DEBUG) {
-                            Slog.d(TAG, "restoreAll() kicking off");
-                        }
-                        Message msg = backupManagerService.getBackupHandler().obtainMessage(
-                                MSG_RUN_RESTORE);
-                        msg.obj = new RestoreParams(mRestoreTransport, dirName,
-                                observer, monitor, token);
-                        backupManagerService.getBackupHandler().sendMessage(msg);
+                        return sendRestoreToHandlerLocked(
+                                (transportClient, listener) ->
+                                        RestoreParams.createForRestoreAll(
+                                                transportClient,
+                                                observer,
+                                                monitor,
+                                                token,
+                                                listener),
+                                "RestoreSession.restoreAll()");
                     } finally {
                         Binder.restoreCallingIdentity(oldId);
                     }
-                    return 0;
                 }
             }
         }
@@ -183,7 +189,7 @@
     // Restores of more than a single package are treated as 'system' restores
     public synchronized int restoreSome(long token, IRestoreObserver observer,
             IBackupManagerMonitor monitor, String[] packages) {
-        backupManagerService.getContext().enforceCallingOrSelfPermission(
+        mBackupManagerService.getContext().enforceCallingOrSelfPermission(
                 android.Manifest.permission.BACKUP,
                 "performRestore");
 
@@ -227,7 +233,7 @@
             return -1;
         }
 
-        if (mRestoreTransport == null || mRestoreSets == null) {
+        if (mRestoreSets == null) {
             Slog.e(TAG, "Ignoring restoreAll() with no restore set");
             return -1;
         }
@@ -237,34 +243,30 @@
             return -1;
         }
 
-        String dirName;
-        try {
-            dirName = mRestoreTransport.transportDirName();
-        } catch (Exception e) {
-            // Transport went AWOL; fail.
-            Slog.e(TAG, "Unable to get transport name for restoreSome: " + e.getMessage());
+        if (!mTransportManager.isTransportRegistered(mTransportName)) {
+            Slog.e(TAG, "Transport " + mTransportName + " not registered");
             return -1;
         }
 
-        synchronized (backupManagerService.getQueueLock()) {
+        synchronized (mBackupManagerService.getQueueLock()) {
             for (int i = 0; i < mRestoreSets.length; i++) {
                 if (token == mRestoreSets[i].token) {
-                    // Stop the session timeout until we finalize the restore
-                    backupManagerService.getBackupHandler().removeMessages(
-                            MSG_RESTORE_SESSION_TIMEOUT);
-
                     long oldId = Binder.clearCallingIdentity();
-                    backupManagerService.getWakelock().acquire();
-                    if (MORE_DEBUG) {
-                        Slog.d(TAG, "restoreSome() of " + packages.length + " packages");
+                    try {
+                        return sendRestoreToHandlerLocked(
+                                (transportClient, listener) ->
+                                        RestoreParams.createForRestoreSome(
+                                                transportClient,
+                                                observer,
+                                                monitor,
+                                                token,
+                                                packages,
+                                                /* isSystemRestore */ packages.length > 1,
+                                                listener),
+                                "RestoreSession.restoreSome(" + packages.length + " packages)");
+                    } finally {
+                        Binder.restoreCallingIdentity(oldId);
                     }
-                    Message msg = backupManagerService.getBackupHandler().obtainMessage(
-                            MSG_RUN_RESTORE);
-                    msg.obj = new RestoreParams(mRestoreTransport, dirName, observer, monitor,
-                            token, packages, packages.length > 1);
-                    backupManagerService.getBackupHandler().sendMessage(msg);
-                    Binder.restoreCallingIdentity(oldId);
-                    return 0;
                 }
             }
         }
@@ -297,9 +299,9 @@
             }
         }
 
-        PackageInfo app = null;
+        final PackageInfo app;
         try {
-            app = backupManagerService.getPackageManager().getPackageInfo(packageName, 0);
+            app = mBackupManagerService.getPackageManager().getPackageInfo(packageName, 0);
         } catch (NameNotFoundException nnf) {
             Slog.w(TAG, "Asked to restore nonexistent pkg " + packageName);
             return -1;
@@ -307,7 +309,7 @@
 
         // If the caller is not privileged and is not coming from the target
         // app's uid, throw a permission exception back to the caller.
-        int perm = backupManagerService.getContext().checkPermission(
+        int perm = mBackupManagerService.getContext().checkPermission(
                 android.Manifest.permission.BACKUP,
                 Binder.getCallingPid(), Binder.getCallingUid());
         if ((perm == PackageManager.PERMISSION_DENIED) &&
@@ -317,12 +319,17 @@
             throw new SecurityException("No permission to restore other packages");
         }
 
+        if (!mTransportManager.isTransportRegistered(mTransportName)) {
+            Slog.e(TAG, "Transport " + mTransportName + " not registered");
+            return -1;
+        }
+
         // So far so good; we're allowed to try to restore this package.
         long oldId = Binder.clearCallingIdentity();
         try {
             // Check whether there is data for it in the current dataset, falling back
             // to the ancestral dataset if not.
-            long token = backupManagerService.getAvailableRestoreToken(packageName);
+            long token = mBackupManagerService.getAvailableRestoreToken(packageName);
             if (DEBUG) {
                 Slog.v(TAG, "restorePackage pkg=" + packageName
                         + " token=" + Long.toHexString(token));
@@ -338,30 +345,53 @@
                 return -1;
             }
 
-            String dirName;
-            try {
-                dirName = mRestoreTransport.transportDirName();
-            } catch (Exception e) {
-                // Transport went AWOL; fail.
-                Slog.e(TAG, "Unable to get transport dir for restorePackage: " + e.getMessage());
-                return -1;
-            }
-
-            // Stop the session timeout until we finalize the restore
-            backupManagerService.getBackupHandler().removeMessages(MSG_RESTORE_SESSION_TIMEOUT);
-
-            // Ready to go:  enqueue the restore request and claim success
-            backupManagerService.getWakelock().acquire();
-            if (MORE_DEBUG) {
-                Slog.d(TAG, "restorePackage() : " + packageName);
-            }
-            Message msg = backupManagerService.getBackupHandler().obtainMessage(MSG_RUN_RESTORE);
-            msg.obj = new RestoreParams(mRestoreTransport, dirName, observer, monitor,
-                    token, app);
-            backupManagerService.getBackupHandler().sendMessage(msg);
+            return sendRestoreToHandlerLocked(
+                    (transportClient, listener) ->
+                            RestoreParams.createForSinglePackage(
+                                    transportClient,
+                                    observer,
+                                    monitor,
+                                    token,
+                                    app,
+                                    listener),
+                    "RestoreSession.restorePackage(" + packageName + ")");
         } finally {
             Binder.restoreCallingIdentity(oldId);
         }
+    }
+
+    /**
+     * Returns 0 if operation sent or -1 otherwise.
+     */
+    private int sendRestoreToHandlerLocked(
+            BiFunction<TransportClient, OnTaskFinishedListener, RestoreParams> restoreParamsBuilder,
+            String callerLogString) {
+        TransportClient transportClient =
+                mTransportManager.getTransportClient(mTransportName, callerLogString);
+        if (transportClient == null) {
+            Slog.e(TAG, "Transport " + mTransportName + " got unregistered");
+            return -1;
+        }
+
+        // Stop the session timeout until we finalize the restore
+        BackupHandler backupHandler = mBackupManagerService.getBackupHandler();
+        backupHandler.removeMessages(MSG_RESTORE_SESSION_TIMEOUT);
+
+        PowerManager.WakeLock wakelock = mBackupManagerService.getWakelock();
+        wakelock.acquire();
+        if (MORE_DEBUG) {
+            Slog.d(TAG, callerLogString);
+        }
+
+        // Prevent lambda from leaking 'this'
+        TransportManager transportManager = mTransportManager;
+        OnTaskFinishedListener listener = caller -> {
+                transportManager.disposeOfTransportClient(transportClient, caller);
+                wakelock.release();
+        };
+        Message msg = backupHandler.obtainMessage(MSG_RUN_RESTORE);
+        msg.obj = restoreParamsBuilder.apply(transportClient, listener);
+        backupHandler.sendMessage(msg);
         return 0;
     }
 
@@ -380,7 +410,6 @@
         public void run() {
             // clean up the session's bookkeeping
             synchronized (mSession) {
-                mSession.mRestoreTransport = null;
                 mSession.mEnded = true;
             }
 
@@ -404,7 +433,7 @@
             throw new IllegalStateException("Restore session already ended");
         }
 
-        backupManagerService.getBackupHandler().post(
-                new EndRestoreRunnable(backupManagerService, this));
+        mBackupManagerService.getBackupHandler().post(
+                new EndRestoreRunnable(mBackupManagerService, this));
     }
 }
diff --git a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
index 2db7430..86866dc 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
@@ -61,6 +61,8 @@
 import com.android.server.backup.PackageManagerBackupAgent;
 import com.android.server.backup.PackageManagerBackupAgent.Metadata;
 import com.android.server.backup.RefactoredBackupManagerService;
+import com.android.server.backup.internal.OnTaskFinishedListener;
+import com.android.server.backup.transport.TransportClient;
 import com.android.server.backup.utils.AppBackupUtils;
 import com.android.server.backup.utils.BackupManagerMonitorUtils;
 
@@ -76,8 +78,8 @@
 public class PerformUnifiedRestoreTask implements BackupRestoreTask {
 
     private RefactoredBackupManagerService backupManagerService;
-    // Transport we're working with to do the restore
-    private IBackupTransport mTransport;
+    // Transport client we're working with to do the restore
+    private final TransportClient mTransportClient;
 
     // Where per-transport saved state goes
     File mStateDir;
@@ -141,6 +143,9 @@
     // Done?
     private boolean mFinished;
 
+    // When finished call listener
+    private final OnTaskFinishedListener mListener;
+
     // Key/value: bookkeeping about staged data and files for agent access
     private File mBackupDataName;
     private File mStageName;
@@ -154,15 +159,16 @@
     // Invariant: mWakelock is already held, and this task is responsible for
     // releasing it at the end of the restore operation.
     public PerformUnifiedRestoreTask(RefactoredBackupManagerService backupManagerService,
-            IBackupTransport transport, IRestoreObserver observer,
+            TransportClient transportClient, IRestoreObserver observer,
             IBackupManagerMonitor monitor, long restoreSetToken, PackageInfo targetPackage,
-            int pmToken, boolean isFullSystemRestore, String[] filterSet) {
+            int pmToken, boolean isFullSystemRestore, String[] filterSet,
+            OnTaskFinishedListener listener) {
         this.backupManagerService = backupManagerService;
         mEphemeralOpToken = backupManagerService.generateRandomIntegerToken();
         mState = UnifiedRestoreState.INITIAL;
         mStartRealtime = SystemClock.elapsedRealtime();
 
-        mTransport = transport;
+        mTransportClient = transportClient;
         mObserver = observer;
         mMonitor = monitor;
         mToken = restoreSetToken;
@@ -171,6 +177,7 @@
         mIsSystemRestore = isFullSystemRestore;
         mFinished = false;
         mDidLaunch = false;
+        mListener = listener;
 
         if (targetPackage != null) {
             // Single package restore
@@ -342,7 +349,7 @@
         }
 
         try {
-            String transportDir = mTransport.transportDirName();
+            String transportDir = mTransportClient.getTransportDirName();
             mStateDir = new File(backupManagerService.getBaseStateDir(), transportDir);
 
             // Fetch the current metadata from the dataset first
@@ -351,7 +358,11 @@
             mAcceptSet.add(0, pmPackage);
 
             PackageInfo[] packages = mAcceptSet.toArray(new PackageInfo[0]);
-            mStatus = mTransport.startRestore(mToken, packages);
+
+            IBackupTransport transport =
+                    mTransportClient.connectOrThrow("PerformUnifiedRestoreTask.startRestore()");
+
+            mStatus = transport.startRestore(mToken, packages);
             if (mStatus != BackupTransport.TRANSPORT_OK) {
                 Slog.e(TAG, "Transport error " + mStatus + "; no restore possible");
                 mStatus = BackupTransport.TRANSPORT_ERROR;
@@ -359,7 +370,7 @@
                 return;
             }
 
-            RestoreDescription desc = mTransport.nextRestorePackage();
+            RestoreDescription desc = transport.nextRestorePackage();
             if (desc == null) {
                 Slog.e(TAG, "No restore metadata available; halting");
                 mMonitor = BackupManagerMonitorUtils.monitorEvent(mMonitor,
@@ -444,7 +455,10 @@
     private void dispatchNextRestore() {
         UnifiedRestoreState nextState = UnifiedRestoreState.FINAL;
         try {
-            mRestoreDescription = mTransport.nextRestorePackage();
+            IBackupTransport transport =
+                    mTransportClient.connectOrThrow(
+                            "PerformUnifiedRestoreTask.dispatchNextRestore()");
+            mRestoreDescription = transport.nextRestorePackage();
             final String pkgName = (mRestoreDescription != null)
                     ? mRestoreDescription.getPackageName() : null;
             if (pkgName == null) {
@@ -657,13 +671,17 @@
         File downloadFile = (staging) ? mStageName : mBackupDataName;
 
         try {
+            IBackupTransport transport =
+                    mTransportClient.connectOrThrow(
+                            "PerformUnifiedRestoreTask.initiateOneRestore()");
+
             // Run the transport's restore pass
             stage = ParcelFileDescriptor.open(downloadFile,
                     ParcelFileDescriptor.MODE_READ_WRITE |
                             ParcelFileDescriptor.MODE_CREATE |
                             ParcelFileDescriptor.MODE_TRUNCATE);
 
-            if (mTransport.getRestoreData(stage) != BackupTransport.TRANSPORT_OK) {
+            if (transport.getRestoreData(stage) != BackupTransport.TRANSPORT_OK) {
                 // Transport-level failure, so we wind everything up and
                 // terminate the restore operation.
                 Slog.e(TAG, "Error getting restore data for " + packageName);
@@ -750,7 +768,7 @@
         // None of this can run on the work looper here, so we spin asynchronous
         // work like this:
         //
-        //   StreamFeederThread: read data from mTransport.getNextFullRestoreDataChunk()
+        //   StreamFeederThread: read data from transport.getNextFullRestoreDataChunk()
         //                       write it into the pipe to the engine
         //   EngineThread: FullRestoreEngine thread communicating with the target app
         //
@@ -844,10 +862,12 @@
             // spin up the engine and start moving data to it
             new Thread(mEngineThread, "unified-restore-engine").start();
 
+            String callerLogString = "PerformUnifiedRestoreTask$StreamFeederThread.run()";
             try {
+                IBackupTransport transport = mTransportClient.connectOrThrow(callerLogString);
                 while (status == BackupTransport.TRANSPORT_OK) {
                     // have the transport write some of the restoring data to us
-                    int result = mTransport.getNextFullRestoreDataChunk(tWriteEnd);
+                    int result = transport.getNextFullRestoreDataChunk(tWriteEnd);
                     if (result > 0) {
                         // The transport wrote this many bytes of restore data to the
                         // pipe, so pass it along to the engine.
@@ -936,7 +956,9 @@
                     // Something went wrong somewhere.  Whether it was at the transport
                     // level is immaterial; we need to tell the transport to bail
                     try {
-                        mTransport.abortFullRestore();
+                        IBackupTransport transport =
+                                mTransportClient.connectOrThrow(callerLogString);
+                        transport.abortFullRestore();
                     } catch (Exception e) {
                         // transport itself is dead; make sure we handle this as a
                         // fatal error
@@ -1039,8 +1061,11 @@
             Slog.d(TAG, "finishing restore mObserver=" + mObserver);
         }
 
+        String callerLogString = "PerformUnifiedRestoreTask.finalizeRestore()";
         try {
-            mTransport.finishRestore();
+            IBackupTransport transport =
+                    mTransportClient.connectOrThrow(callerLogString);
+            transport.finishRestore();
         } catch (Exception e) {
             Slog.e(TAG, "Error finishing restore", e);
         }
@@ -1087,9 +1112,6 @@
             backupManagerService.writeRestoreTokens();
         }
 
-        // done; we can finally release the wakelock and be legitimately done.
-        Slog.i(TAG, "Restore complete.");
-
         synchronized (backupManagerService.getPendingRestores()) {
             if (backupManagerService.getPendingRestores().size() > 0) {
                 if (DEBUG) {
@@ -1108,7 +1130,8 @@
             }
         }
 
-        backupManagerService.getWakelock().release();
+        Slog.i(TAG, "Restore complete.");
+        mListener.onFinished(callerLogString);
     }
 
     void keyValueAgentErrorCleanup() {
@@ -1301,13 +1324,11 @@
 
     void sendOnRestorePackage(String name) {
         if (mObserver != null) {
-            if (mObserver != null) {
-                try {
-                    mObserver.onUpdate(mCount, name);
-                } catch (RemoteException e) {
-                    Slog.d(TAG, "Restore observer died in onUpdate");
-                    mObserver = null;
-                }
+            try {
+                mObserver.onUpdate(mCount, name);
+            } catch (RemoteException e) {
+                Slog.d(TAG, "Restore observer died in onUpdate");
+                mObserver = null;
             }
         }
     }
diff --git a/services/backup/java/com/android/server/backup/restore/RestoreEngine.java b/services/backup/java/com/android/server/backup/restore/RestoreEngine.java
index b2fdbb8..9d3ae86 100644
--- a/services/backup/java/com/android/server/backup/restore/RestoreEngine.java
+++ b/services/backup/java/com/android/server/backup/restore/RestoreEngine.java
@@ -30,8 +30,8 @@
     public static final int TARGET_FAILURE = -2;
     public static final int TRANSPORT_FAILURE = -3;
 
-    private AtomicBoolean mRunning = new AtomicBoolean(false);
-    private AtomicInteger mResult = new AtomicInteger(SUCCESS);
+    private final AtomicBoolean mRunning = new AtomicBoolean(false);
+    private final AtomicInteger mResult = new AtomicInteger(SUCCESS);
 
     public boolean isRunning() {
         return mRunning.get();
diff --git a/services/backup/java/com/android/server/backup/transport/TransportUtils.java b/services/backup/java/com/android/server/backup/transport/TransportUtils.java
index 85599b7..92bba9b 100644
--- a/services/backup/java/com/android/server/backup/transport/TransportUtils.java
+++ b/services/backup/java/com/android/server/backup/transport/TransportUtils.java
@@ -31,7 +31,7 @@
      * Throws {@link TransportNotAvailableException} if {@param transport} is null. The semantics is
      * similar to a {@link DeadObjectException} coming from a dead transport binder.
      */
-    public static IBackupTransport checkTransport(@Nullable IBackupTransport transport)
+    public static IBackupTransport checkTransportNotNull(@Nullable IBackupTransport transport)
             throws TransportNotAvailableException {
         if (transport == null) {
             log(Log.ERROR, TAG, "Transport not available");
diff --git a/services/core/java/com/android/server/am/ActivityDisplay.java b/services/core/java/com/android/server/am/ActivityDisplay.java
index 9bfdd0c..af5cf1e 100644
--- a/services/core/java/com/android/server/am/ActivityDisplay.java
+++ b/services/core/java/com/android/server/am/ActivityDisplay.java
@@ -409,15 +409,16 @@
                 otherStack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
             }
         } finally {
-            if (mHomeStack != null && !isTopStack(mHomeStack)) {
+            final ActivityStack topFullscreenStack =
+                    getStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+            if (topFullscreenStack != null && mHomeStack != null && !isTopStack(mHomeStack)) {
                 // Whenever split-screen is dismissed we want the home stack directly behind the
-                // currently top stack so it shows up when the top stack is finished.
-                final ActivityStack topStack = getTopStack();
+                // current top fullscreen stack so it shows up when the top stack is finished.
                 // TODO: Would be better to use ActivityDisplay.positionChildAt() for this, however
                 // ActivityDisplay doesn't have a direct controller to WM side yet. We can switch
                 // once we have that.
                 mHomeStack.moveToFront("onSplitScreenModeDismissed");
-                topStack.moveToFront("onSplitScreenModeDismissed");
+                topFullscreenStack.moveToFront("onSplitScreenModeDismissed");
             }
             mSupervisor.mWindowManager.continueSurfaceLayout();
         }
@@ -435,7 +436,7 @@
                 }
                 otherStack.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY,
                         false /* animate */, false /* showRecents */,
-                        false /* sendNonResizeableNotification */);
+                        true /* enteringSplitScreenMode */);
             }
         } finally {
             mSupervisor.mWindowManager.continueSurfaceLayout();
@@ -555,6 +556,16 @@
         return stack == getTopStack();
     }
 
+    boolean isTopFullscreenStack(ActivityStack stack) {
+        for (int i = mStacks.size() - 1; i >= 0; --i) {
+            final ActivityStack current = mStacks.get(i);
+            if (current.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) {
+                return current == stack;
+            }
+        }
+        return false;
+    }
+
     int getIndexOf(ActivityStack stack) {
         return mStacks.indexOf(stack);
     }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index bcb489d..9366f6e 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -10591,7 +10591,7 @@
                     stack.moveToFront("setTaskWindowingModeSplitScreenPrimary", task);
                 }
                 stack.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, animate, showRecents,
-                        true /* sendNonResizeableNotification */);
+                        false /* enteringSplitScreenMode */);
                 return windowingMode != task.getWindowingMode();
             } finally {
                 Binder.restoreCallingIdentity(ident);
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index d42c5de..69f6d5e 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -2731,7 +2731,8 @@
      *         {@link #mShowWhenLocked}.
      */
     boolean canShowWhenLocked() {
-        return mShowWhenLocked || service.mWindowManager.containsShowWhenLockedWindow(appToken);
+        return !inMultiWindowMode() && (mShowWhenLocked
+                || service.mWindowManager.containsShowWhenLockedWindow(appToken));
     }
 
     void setTurnScreenOn(boolean turnScreenOn) {
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index cf40be5..10c801d 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -484,20 +484,24 @@
     @Override
     public void setWindowingMode(int windowingMode) {
         setWindowingMode(windowingMode, false /* animate */, true /* showRecents */,
-                true /* sendNonResizeableNotification */);
+                false /* enteringSplitScreenMode */);
     }
 
     void setWindowingMode(int preferredWindowingMode, boolean animate, boolean showRecents,
-            boolean sendNonResizeableNotification) {
+            boolean enteringSplitScreenMode) {
+        final boolean creating = mWindowContainerController == null;
         final int currentMode = getWindowingMode();
         final ActivityDisplay display = getDisplay();
         final TaskRecord topTask = topTask();
         final ActivityStack splitScreenStack = display.getSplitScreenPrimaryStack();
         mTmpOptions.setLaunchWindowingMode(preferredWindowingMode);
 
-        // Need to make sure windowing mode is supported.
-        int windowingMode = display.resolveWindowingMode(
-                null /* ActivityRecord */, mTmpOptions, topTask, getActivityType());
+        // Need to make sure windowing mode is supported. If we in the process of creating the stack
+        // no need to resolve the windowing mode again as it is already resolved to the right mode.
+        int windowingMode = creating
+                ? preferredWindowingMode
+                : display.resolveWindowingMode(
+                        null /* ActivityRecord */, mTmpOptions, topTask, getActivityType());
         if (splitScreenStack == this && windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) {
             // Resolution to split-screen secondary for the primary split-screen stack means we want
             // to go fullscreen.
@@ -506,14 +510,19 @@
 
         final boolean alreadyInSplitScreenMode = display.hasSplitScreenPrimaryStack();
 
+        // Don't send non-resizeable notifications if the windowing mode changed was a side effect
+        // of us entering split-screen mode.
+        final boolean sendNonResizeableNotification = !enteringSplitScreenMode;
         // Take any required action due to us not supporting the preferred windowing mode.
-        if (sendNonResizeableNotification
-                && windowingMode != preferredWindowingMode && isActivityTypeStandardOrUndefined()) {
-            if (alreadyInSplitScreenMode
-                    && (preferredWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
-                    || preferredWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY)) {
-                // Looks like we can't launch in split screen mode, go ahead an dismiss split-screen
-                // and display a warning toast about it.
+        if (alreadyInSplitScreenMode && windowingMode == WINDOWING_MODE_FULLSCREEN
+                && sendNonResizeableNotification && isActivityTypeStandardOrUndefined()) {
+            final boolean preferredSplitScreen =
+                    preferredWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
+                    || preferredWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+            if (preferredSplitScreen || creating) {
+                // Looks like we can't launch in split screen mode or the stack we are launching
+                // doesn't support split-screen mode, go ahead an dismiss split-screen and display a
+                // warning toast about it.
                 mService.mTaskChangeNotificationController.notifyActivityDismissingDockedStack();
                 display.getSplitScreenPrimaryStack().setWindowingMode(WINDOWING_MODE_FULLSCREEN);
             }
@@ -544,7 +553,7 @@
             }
             super.setWindowingMode(windowingMode);
 
-            if (mWindowContainerController == null) {
+            if (creating) {
                 // Nothing else to do if we don't have a window container yet. E.g. call from ctor.
                 return;
             }
@@ -594,16 +603,22 @@
                 // the one where the home stack is visible since recents isn't visible yet, but the
                 // divider will be off. I think we should just make the initial bounds that of home
                 // so that the divider matches and remove this logic.
-                display.getOrCreateStack(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY,
-                        ACTIVITY_TYPE_RECENTS, true /* onTop */);
+                final ActivityStack recentStack = display.getOrCreateStack(
+                        WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_RECENTS,
+                        true /* onTop */);
+                recentStack.moveToFront("setWindowingMode");
                 // If task moved to docked stack - show recents if needed.
                 mService.mWindowManager.showRecentApps(false /* fromHome */);
             }
             wm.continueSurfaceLayout();
         }
 
-        mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, PRESERVE_WINDOWS);
-        mStackSupervisor.resumeFocusedStackTopActivityLocked();
+        // Don't ensure visible activities if the windowing mode change was a side effect of us
+        // entering split-screen mode.
+        if (!enteringSplitScreenMode) {
+            mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, PRESERVE_WINDOWS);
+            mStackSupervisor.resumeFocusedStackTopActivityLocked();
+        }
     }
 
     @Override
@@ -1668,7 +1683,7 @@
                     continue;
                 }
 
-                if (!r.visible && r != starting) {
+                if (!r.visibleIgnoringKeyguard && r != starting) {
                     // Also ignore invisible activities that are not the currently starting
                     // activity (about to be visible).
                     continue;
@@ -1796,6 +1811,7 @@
             boolean behindFullscreenActivity = !stackShouldBeVisible;
             boolean resumeNextActivity = mStackSupervisor.isFocusedStack(this)
                     && (isInStackLocked(starting) == null);
+            final boolean isTopFullscreenStack = getDisplay().isTopFullscreenStack(this);
             for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
                 final TaskRecord task = mTaskHistory.get(taskNdx);
                 final ArrayList<ActivityRecord> activities = task.mActivities;
@@ -1817,7 +1833,7 @@
 
                     // Now check whether it's really visible depending on Keyguard state.
                     final boolean reallyVisible = checkKeyguardVisibility(r,
-                            visibleIgnoringKeyguard, isTop);
+                            visibleIgnoringKeyguard, isTop && isTopFullscreenStack);
                     if (visibleIgnoringKeyguard) {
                         behindFullscreenActivity = updateBehindFullscreen(!stackShouldBeVisible,
                                 behindFullscreenActivity, r);
@@ -1943,13 +1959,11 @@
      *
      * @return true if {@param r} is visible taken Keyguard state into account, false otherwise
      */
-    boolean checkKeyguardVisibility(ActivityRecord r, boolean shouldBeVisible,
-            boolean isTop) {
-        final boolean isInPinnedStack = r.inPinnedWindowingMode();
+    boolean checkKeyguardVisibility(ActivityRecord r, boolean shouldBeVisible, boolean isTop) {
         final boolean keyguardShowing = mStackSupervisor.getKeyguardController().isKeyguardShowing(
                 mDisplayId != INVALID_DISPLAY ? mDisplayId : DEFAULT_DISPLAY);
         final boolean keyguardLocked = mStackSupervisor.getKeyguardController().isKeyguardLocked();
-        final boolean showWhenLocked = r.canShowWhenLocked() && !isInPinnedStack;
+        final boolean showWhenLocked = r.canShowWhenLocked();
         final boolean dismissKeyguard = r.hasDismissKeyguardWindows();
         if (shouldBeVisible) {
             if (dismissKeyguard && mTopDismissingKeyguardActivity == null) {
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index c39ce98..94a0cb7 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -66,6 +66,7 @@
 import android.view.SurfaceControl;
 import android.view.WindowManager;
 import android.view.WindowManager.LayoutParams;
+import android.view.animation.Transformation;
 
 import com.android.internal.util.ToBooleanFunction;
 import com.android.server.input.InputApplicationHandle;
@@ -75,6 +76,7 @@
 import java.io.PrintWriter;
 import java.util.ArrayDeque;
 import java.util.ArrayList;
+import java.util.LinkedList;
 
 class AppTokenList extends ArrayList<AppWindowToken> {
 }
@@ -231,7 +233,7 @@
             // If this initial window is animating, stop it -- we will do an animation to reveal
             // it from behind the starting window, so there is no need for it to also be doing its
             // own stuff.
-            winAnimator.clearAnimation();
+            win.cancelAnimation();
             if (getController() != null) {
                 getController().removeStartingWindow();
             }
@@ -389,7 +391,7 @@
         }
 
         for (int i = mChildren.size() - 1; i >= 0 && !delayed; i--) {
-            if ((mChildren.get(i)).isWindowAnimationSet()) {
+            if ((mChildren.get(i)).isSelfOrChildAnimating()) {
                 delayed = true;
             }
         }
@@ -610,8 +612,12 @@
      */
     private void destroySurfaces(boolean cleanupOnResume) {
         boolean destroyedSomething = false;
-        for (int i = mChildren.size() - 1; i >= 0; i--) {
-            final WindowState win = mChildren.get(i);
+
+        // Copying to a different list as multiple children can be removed.
+        // TODO: Not sure why this is needed.
+        final LinkedList<WindowState> children = new LinkedList<>(mChildren);
+        for (int i = children.size() - 1; i >= 0; i--) {
+            final WindowState win = children.get(i);
             destroyedSomething |= win.destroySurface(cleanupOnResume, mAppStopped);
         }
         if (destroyedSomething) {
@@ -1320,7 +1326,7 @@
                             + " pv=" + w.mPolicyVisibility
                             + " mDrawState=" + winAnimator.drawStateToString()
                             + " ph=" + w.isParentWindowHidden() + " th=" + hiddenRequested
-                            + " a=" + winAnimator.mAnimating);
+                            + " a=" + winAnimator.isAnimationSet());
                 }
             }
 
@@ -1520,6 +1526,11 @@
     }
 
     @Override
+    boolean isSelfAnimating() {
+        return mAppAnimator.isAnimating();
+    }
+
+    @Override
     void dump(PrintWriter pw, String prefix) {
         super.dump(pw, prefix);
         if (appToken != null) {
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index eda8fec..f458457 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -146,6 +146,7 @@
 import android.view.MagnificationSpec;
 import android.view.Surface;
 import android.view.SurfaceControl;
+import android.view.SurfaceControl.Transaction;
 import android.view.SurfaceSession;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -339,6 +340,7 @@
             new ApplySurfaceChangesTransactionState();
     private final ScreenshotApplicationState mScreenshotApplicationState =
             new ScreenshotApplicationState();
+    private final Transaction mTmpTransaction = new Transaction();
 
     // True if this display is in the process of being removed. Used to determine if the removal of
     // the display's direct children should be allowed.
@@ -379,29 +381,11 @@
      */
     private final ArrayList<SurfaceControl> mPendingDestroyingSurfaces = new ArrayList<>();
 
+    /** Temporary float array to retrieve 3x3 matrix values. */
+    private final float[] mTmpFloats = new float[9];
+
     private final Consumer<WindowState> mUpdateWindowsForAnimator = w -> {
         WindowStateAnimator winAnimator = w.mWinAnimator;
-        if (winAnimator.hasSurface()) {
-            final boolean wasAnimating = winAnimator.mWasAnimating;
-            final boolean nowAnimating = winAnimator.stepAnimationLocked(
-                    mTmpWindowAnimator.mCurrentTime);
-            winAnimator.mWasAnimating = nowAnimating;
-            mTmpWindowAnimator.orAnimating(nowAnimating);
-
-            if (DEBUG_WALLPAPER) Slog.v(TAG,
-                    w + ": wasAnimating=" + wasAnimating + ", nowAnimating=" + nowAnimating);
-
-            if (wasAnimating && !winAnimator.mAnimating
-                    && mWallpaperController.isWallpaperTarget(w)) {
-                mTmpWindowAnimator.mBulkUpdateParams |= SET_WALLPAPER_MAY_CHANGE;
-                pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
-                if (DEBUG_LAYOUT_REPEATS) {
-                    mService.mWindowPlacerLocked.debugLayoutRepeats(
-                            "updateWindowsAndWallpaperLocked 2", pendingLayoutChanges);
-                }
-            }
-        }
-
         final AppWindowToken atoken = w.mAppToken;
         if (winAnimator.mDrawState == READY_TO_SHOW) {
             if (atoken == null || atoken.allDrawn) {
@@ -434,13 +418,13 @@
 
         // If this window is animating, make a note that we have an animating window and take
         // care of a request to run a detached wallpaper animation.
-        if (winAnimator.mAnimating) {
-            if (winAnimator.mAnimation != null) {
-                if ((flags & FLAG_SHOW_WALLPAPER) != 0
-                        && winAnimator.mAnimation.getDetachWallpaper()) {
+        if (winAnimator.isAnimationSet()) {
+            final AnimationAdapter anim = w.getAnimation();
+            if (anim != null) {
+                if ((flags & FLAG_SHOW_WALLPAPER) != 0 && anim.getDetachWallpaper()) {
                     mTmpWindow = w;
                 }
-                final int color = winAnimator.mAnimation.getBackgroundColor();
+                final int color = anim.getBackgroundColor();
                 if (color != 0) {
                     final TaskStack stack = w.getStack();
                     if (stack != null) {
@@ -448,7 +432,6 @@
                     }
                 }
             }
-            mTmpWindowAnimator.setAnimating(true);
         }
 
         // If this window's app token is running a detached wallpaper animation, make a note so
@@ -684,7 +667,10 @@
             mWallpaperController.updateWallpaperVisibility();
         }
 
-        w.handleWindowMovedIfNeeded();
+        // Use mTmpTransaction instead of mPendingTransaction because we don't want to commit
+        // other changes in mPendingTransaction at this point.
+        w.handleWindowMovedIfNeeded(mTmpTransaction);
+        SurfaceControl.mergeToGlobalTransaction(mTmpTransaction);
 
         final WindowStateAnimator winAnimator = w.mWinAnimator;
 
@@ -720,7 +706,7 @@
                 }
             }
             final TaskStack stack = w.getStack();
-            if ((!winAnimator.isAnimationStarting() && !winAnimator.isWaitingForOpening())
+            if ((!winAnimator.isWaitingForOpening())
                     || (stack != null && stack.isAnimatingBounds())) {
                 // Updates the shown frame before we set up the surface. This is needed
                 // because the resizing could change the top-left position (in addition to
@@ -736,6 +722,12 @@
                 winAnimator.computeShownFrameLocked();
             }
             winAnimator.setSurfaceBoundariesLocked(mTmpRecoveringMemory /* recoveringMemory */);
+
+            // Since setSurfaceBoundariesLocked applies the clipping, we need to apply the position
+            // to the surface of the window container as well. Use mTmpTransaction instead of
+            // mPendingTransaction to avoid committing any existing changes in there.
+            w.updateSurfacePosition(mTmpTransaction);
+            SurfaceControl.mergeToGlobalTransaction(mTmpTransaction);
         }
 
         final AppWindowToken atoken = w.mAppToken;
@@ -2617,8 +2609,7 @@
         forAllWindows(w -> {
             if (w.mAppToken == null && policy.canBeHiddenByKeyguardLw(w)
                     && w.wouldBeVisibleIfPolicyIgnored() && !w.isVisible()) {
-                w.mWinAnimator.setAnimation(
-                        policy.createHiddenByKeyguardExit(onWallpaper, goingToShade));
+                w.startAnimation(policy.createHiddenByKeyguardExit(onWallpaper, goingToShade));
             }
         }, true /* traverseTopToBottom */);
     }
@@ -3775,13 +3766,13 @@
     }
 
     @Override
-    void destroyAfterPendingTransaction(SurfaceControl surface) {
+    public void destroyAfterPendingTransaction(SurfaceControl surface) {
         mPendingDestroyingSurfaces.add(surface);
     }
 
     /**
      * Destroys any surfaces that have been put into the pending list with
-     * {@link #destroyAfterTransaction}.
+     * {@link #destroyAfterPendingTransaction}.
      */
     void onPendingTransactionApplied() {
         for (int i = mPendingDestroyingSurfaces.size() - 1; i >= 0; i--) {
@@ -3789,4 +3780,21 @@
         }
         mPendingDestroyingSurfaces.clear();
     }
+
+    @Override
+    void prepareSurfaces() {
+        final ScreenRotationAnimation screenRotationAnimation =
+                mService.mAnimator.getScreenRotationAnimationLocked(mDisplayId);
+        if (screenRotationAnimation != null && screenRotationAnimation.isAnimating()) {
+            screenRotationAnimation.getEnterTransformation().getMatrix().getValues(mTmpFloats);
+            mPendingTransaction.setMatrix(mWindowingLayer,
+                    mTmpFloats[Matrix.MSCALE_X], mTmpFloats[Matrix.MSKEW_Y],
+                    mTmpFloats[Matrix.MSKEW_X], mTmpFloats[Matrix.MSCALE_Y]);
+            mPendingTransaction.setPosition(mWindowingLayer,
+                    mTmpFloats[Matrix.MTRANS_X], mTmpFloats[Matrix.MTRANS_Y]);
+            mPendingTransaction.setAlpha(mWindowingLayer,
+                    screenRotationAnimation.getEnterTransformation().getAlpha());
+        }
+        super.prepareSurfaces();
+    }
 }
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index a37598e..03c0768 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -751,10 +751,10 @@
 
                 // There might be an old window delaying the animation start - clear it.
                 if (mDelayedImeWin != null) {
-                    mDelayedImeWin.mWinAnimator.endDelayingAnimationStart();
+                    mDelayedImeWin.endDelayingAnimationStart();
                 }
                 mDelayedImeWin = imeWin;
-                imeWin.mWinAnimator.startDelayingAnimationStart();
+                imeWin.startDelayingAnimationStart();
             }
 
             // If we are already waiting for something to be drawn, clear out the old one so it
@@ -765,25 +765,27 @@
                 mService.mWaitingForDrawnCallback.run();
             }
             mService.mWaitingForDrawnCallback = () -> {
-                mAnimationStartDelayed = false;
-                if (mDelayedImeWin != null) {
-                    mDelayedImeWin.mWinAnimator.endDelayingAnimationStart();
+                synchronized (mService.mWindowMap) {
+                    mAnimationStartDelayed = false;
+                    if (mDelayedImeWin != null) {
+                        mDelayedImeWin.endDelayingAnimationStart();
+                    }
+                    // If the adjust status changed since this was posted, only notify
+                    // the new states and don't animate.
+                    long duration = 0;
+                    if (mAdjustedForIme == adjustedForIme
+                            && mAdjustedForDivider == adjustedForDivider) {
+                        duration = IME_ADJUST_ANIM_DURATION;
+                    } else {
+                        Slog.w(TAG, "IME adjust changed while waiting for drawn:"
+                                + " adjustedForIme=" + adjustedForIme
+                                + " adjustedForDivider=" + adjustedForDivider
+                                + " mAdjustedForIme=" + mAdjustedForIme
+                                + " mAdjustedForDivider=" + mAdjustedForDivider);
+                    }
+                    notifyAdjustedForImeChanged(
+                            mAdjustedForIme || mAdjustedForDivider, duration);
                 }
-                // If the adjust status changed since this was posted, only notify
-                // the new states and don't animate.
-                long duration = 0;
-                if (mAdjustedForIme == adjustedForIme
-                        && mAdjustedForDivider == adjustedForDivider) {
-                    duration = IME_ADJUST_ANIM_DURATION;
-                } else {
-                    Slog.w(TAG, "IME adjust changed while waiting for drawn:"
-                            + " adjustedForIme=" + adjustedForIme
-                            + " adjustedForDivider=" + adjustedForDivider
-                            + " mAdjustedForIme=" + mAdjustedForIme
-                            + " mAdjustedForDivider=" + mAdjustedForDivider);
-                }
-                notifyAdjustedForImeChanged(
-                        mAdjustedForIme || mAdjustedForDivider, duration);
             };
         } else {
             notifyAdjustedForImeChanged(
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java b/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
index 5bc2722..3ef9b3f 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
@@ -29,7 +29,6 @@
 import android.view.Choreographer;
 import android.view.SurfaceControl;
 import android.view.SurfaceControl.Transaction;
-import android.view.animation.Transformation;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
@@ -44,12 +43,19 @@
 
     private final Object mLock = new Object();
 
+    /**
+     * Lock for cancelling animations. Must be acquired on it's own, or after acquiring
+     * {@link #mLock}
+     */
+    private final Object mCancelLock = new Object();
+
     @VisibleForTesting
     Choreographer mChoreographer;
 
     private final Runnable mApplyTransactionRunnable = this::applyTransaction;
     private final AnimationHandler mAnimationHandler;
     private final Transaction mFrameTransaction;
+    private final AnimatorFactory mAnimatorFactory;
     private boolean mApplyScheduled;
 
     @GuardedBy("mLock")
@@ -58,15 +64,15 @@
 
     @GuardedBy("mLock")
     @VisibleForTesting
-    final ArrayMap<SurfaceControl, ValueAnimator> mRunningAnimations = new ArrayMap<>();
+    final ArrayMap<SurfaceControl, RunningAnimation> mRunningAnimations = new ArrayMap<>();
 
     SurfaceAnimationRunner() {
-        this(null /* callbackProvider */, new Transaction());
+        this(null /* callbackProvider */, null /* animatorFactory */, new Transaction());
     }
 
     @VisibleForTesting
     SurfaceAnimationRunner(@Nullable AnimationFrameCallbackProvider callbackProvider,
-            Transaction frameTransaction) {
+            AnimatorFactory animatorFactory, Transaction frameTransaction) {
         SurfaceAnimationThread.getHandler().runWithScissors(() -> mChoreographer = getSfInstance(),
                 0 /* timeout */);
         mFrameTransaction = frameTransaction;
@@ -74,6 +80,9 @@
         mAnimationHandler.setProvider(callbackProvider != null
                 ? callbackProvider
                 : new SfVsyncFrameCallbackProvider(mChoreographer));
+        mAnimatorFactory = animatorFactory != null
+                ? animatorFactory
+                : SfValueAnimator::new;
     }
 
     void startAnimation(AnimationSpec a, SurfaceControl animationLeash, Transaction t,
@@ -94,18 +103,17 @@
         synchronized (mLock) {
             if (mPendingAnimations.containsKey(leash)) {
                 mPendingAnimations.remove(leash);
-                // TODO: Releasing the leash is problematic if reparenting hasn't happened yet.
-                // Fix with transaction
-                //leash.release();
                 return;
             }
-            final ValueAnimator anim = mRunningAnimations.get(leash);
+            final RunningAnimation anim = mRunningAnimations.get(leash);
             if (anim != null) {
                 mRunningAnimations.remove(leash);
+                synchronized (mCancelLock) {
+                    anim.mCancelled = true;
+                }
                 SurfaceAnimationThread.getHandler().post(() -> {
-                    anim.cancel();
+                    anim.mAnim.cancel();
                     applyTransaction();
-                    //leash.release();
                 });
             }
         }
@@ -119,54 +127,51 @@
     }
 
     private void startAnimationLocked(RunningAnimation a) {
-        final ValueAnimator result = new SfValueAnimator();
+        final ValueAnimator anim = mAnimatorFactory.makeAnimator();
 
         // Animation length is already expected to be scaled.
-        result.overrideDurationScale(1.0f);
-        result.setDuration(a.animSpec.getDuration());
-        result.addUpdateListener(animation -> {
-            applyTransformation(a, mFrameTransaction, result.getCurrentPlayTime());
+        anim.overrideDurationScale(1.0f);
+        anim.setDuration(a.mAnimSpec.getDuration());
+        anim.addUpdateListener(animation -> {
+            synchronized (mCancelLock) {
+                if (!a.mCancelled) {
+                    applyTransformation(a, mFrameTransaction, anim.getCurrentPlayTime());
+                }
+            }
 
             // Transaction will be applied in the commit phase.
             scheduleApplyTransaction();
         });
-        result.addListener(new AnimatorListenerAdapter() {
-
-            private boolean mCancelled;
-
+        anim.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationStart(Animator animation) {
-                mFrameTransaction.show(a.leash);
-            }
-
-            @Override
-            public void onAnimationCancel(Animator animation) {
-                mCancelled = true;
+                synchronized (mCancelLock) {
+                    if (!a.mCancelled) {
+                        mFrameTransaction.show(a.mLeash);
+                    }
+                }
             }
 
             @Override
             public void onAnimationEnd(Animator animation) {
                 synchronized (mLock) {
-                    mRunningAnimations.remove(a.leash);
-                }
-                if (!mCancelled) {
-                    // Post on other thread that we can push final state without jank.
-                    AnimationThread.getHandler().post(() -> {
-                        a.finishCallback.run();
-
-                        // Make sure to release the leash after finishCallback has been invoked such
-                        // that reparenting is done already when releasing the leash.
-                        a.leash.release();
-                    });
+                    mRunningAnimations.remove(a.mLeash);
+                    synchronized (mCancelLock) {
+                        if (!a.mCancelled) {
+                            // Post on other thread that we can push final state without jank.
+                            AnimationThread.getHandler().post(a.mFinishCallback);
+                        }
+                    }
                 }
             }
         });
-        result.start();
-        mRunningAnimations.put(a.leash, result);
+        anim.start();
+        a.mAnim = anim;
+        mRunningAnimations.put(a.mLeash, a);
     }
 
     private void applyTransformation(RunningAnimation a, Transaction t, long currentPlayTime) {
-        a.animSpec.apply(t, a.leash, currentPlayTime);
+        a.mAnimSpec.apply(t, a.mLeash, currentPlayTime);
     }
 
     private void stepAnimation(long frameTimeNanos) {
@@ -189,17 +194,26 @@
     }
 
     private static final class RunningAnimation {
-        final AnimationSpec animSpec;
-        final SurfaceControl leash;
-        final Runnable finishCallback;
+        final AnimationSpec mAnimSpec;
+        final SurfaceControl mLeash;
+        final Runnable mFinishCallback;
+        ValueAnimator mAnim;
+
+        @GuardedBy("mCancelLock")
+        private boolean mCancelled;
 
         RunningAnimation(AnimationSpec animSpec, SurfaceControl leash, Runnable finishCallback) {
-            this.animSpec = animSpec;
-            this.leash = leash;
-            this.finishCallback = finishCallback;
+            mAnimSpec = animSpec;
+            mLeash = leash;
+            mFinishCallback = finishCallback;
         }
     }
 
+    @VisibleForTesting
+    interface AnimatorFactory {
+        ValueAnimator makeAnimator();
+    }
+
     /**
      * Value animator that uses sf-vsync signal to tick.
      */
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java
index 713d58b..e165211 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimator.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimator.java
@@ -97,7 +97,7 @@
     void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden) {
         cancelAnimation(t, true /* restarting */);
         mAnimation = anim;
-        final SurfaceControl surface = mAnimatable.getSurface();
+        final SurfaceControl surface = mAnimatable.getSurfaceControl();
         if (surface == null) {
             Slog.w(TAG, "Unable to start animation, surface is null or no children.");
             cancelAnimation();
@@ -105,7 +105,7 @@
         }
         mLeash = createAnimationLeash(surface, t,
                 mAnimatable.getSurfaceWidth(), mAnimatable.getSurfaceHeight(), hidden);
-        mAnimatable.onLeashCreated(t, mLeash);
+        mAnimatable.onAnimationLeashCreated(t, mLeash);
         if (mAnimationStartDelayed) {
             if (DEBUG_ANIM) Slog.i(TAG, "Animation start delayed");
             return;
@@ -169,7 +169,16 @@
      * surface is reparented to the leash. This method takes care of that.
      */
     void setLayer(Transaction t, int layer) {
-        t.setLayer(mLeash != null ? mLeash : mAnimatable.getSurface(), layer);
+        t.setLayer(mLeash != null ? mLeash : mAnimatable.getSurfaceControl(), layer);
+    }
+
+    /**
+     * Sets the surface to be relatively layered.
+     *
+     * @see #setLayer
+     */
+    void setRelativeLayer(Transaction t, SurfaceControl relativeTo, int layer) {
+        t.setRelativeLayer(mLeash != null ? mLeash : mAnimatable.getSurfaceControl(), relativeTo, layer);
     }
 
     /**
@@ -178,7 +187,7 @@
      * @see #setLayer
      */
     void reparent(Transaction t, SurfaceControl newParent) {
-        t.reparent(mLeash != null ? mLeash : mAnimatable.getSurface(), newParent.getHandle());
+        t.reparent(mLeash != null ? mLeash : mAnimatable.getSurfaceControl(), newParent.getHandle());
     }
 
     /**
@@ -207,8 +216,8 @@
     }
 
     private void reset(Transaction t) {
-        final SurfaceControl surface = mAnimatable.getSurface();
-        final SurfaceControl parent = mAnimatable.getParentSurface();
+        final SurfaceControl surface = mAnimatable.getSurfaceControl();
+        final SurfaceControl parent = mAnimatable.getParentSurfaceControl();
 
         // If the surface was destroyed, we don't care to reparent it back.
         final boolean destroy = mLeash != null && surface != null && parent != null;
@@ -216,19 +225,22 @@
             if (DEBUG_ANIM) Slog.i(TAG, "Reparenting to original parent");
             t.reparent(surface, parent.getHandle());
         }
+        if (mLeash != null) {
+            mAnimatable.destroyAfterPendingTransaction(mLeash);
+        }
         mLeash = null;
         mAnimation = null;
 
         // Make sure to inform the animatable after the leash was destroyed.
         if (destroy) {
-            mAnimatable.onLeashDestroyed(t);
+            mAnimatable.onAnimationLeashDestroyed(t);
         }
     }
 
     private SurfaceControl createAnimationLeash(SurfaceControl surface, Transaction t, int width,
             int height, boolean hidden) {
         if (DEBUG_ANIM) Slog.i(TAG, "Reparenting to leash");
-        final SurfaceControl.Builder builder = mAnimatable.makeLeash()
+        final SurfaceControl.Builder builder = mAnimatable.makeAnimationLeash()
                 .setName(surface + " - animation-leash")
                 .setSize(width, height);
         final SurfaceControl leash = builder.build();
@@ -273,7 +285,7 @@
          * @param t The transaction to use to apply any necessary changes.
          * @param leash The leash that was created.
          */
-        void onLeashCreated(Transaction t, SurfaceControl leash);
+        void onAnimationLeashCreated(Transaction t, SurfaceControl leash);
 
         /**
          * Called when the leash is being destroyed, and the surface was reparented back to the
@@ -281,22 +293,30 @@
          *
          * @param t The transaction to use to apply any necessary changes.
          */
-        void onLeashDestroyed(Transaction t);
+        void onAnimationLeashDestroyed(Transaction t);
 
         /**
-         * @return A new child surface.
+         * Destroy a given surface after executing {@link #getPendingTransaction}.
+         *
+         * @see WindowContainer#destroyAfterPendingTransaction
          */
-        SurfaceControl.Builder makeLeash();
+        void destroyAfterPendingTransaction(SurfaceControl surface);
+
+        /**
+         * @return A new surface to be used for the animation leash, inserted at the correct
+         *         position in the hierarchy.
+         */
+        SurfaceControl.Builder makeAnimationLeash();
 
         /**
          * @return The surface of the object to be animated.
          */
-        @Nullable SurfaceControl getSurface();
+        @Nullable SurfaceControl getSurfaceControl();
 
         /**
          * @return The parent of the surface object to be animated.
          */
-        @Nullable SurfaceControl getParentSurface();
+        @Nullable SurfaceControl getParentSurfaceControl();
 
         /**
          * @return The width of the surface to be animated.
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 6ea8a47..244eb66 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -22,7 +22,6 @@
 import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PRESERVE_ORIENTATION;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
 import static android.content.res.Configuration.EMPTY;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
 
 import static com.android.server.EventLogTags.WM_TASK_REMOVED;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK;
@@ -43,7 +42,6 @@
 import android.util.EventLog;
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
-import android.view.DisplayInfo;
 import android.view.Surface;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -162,7 +160,7 @@
     boolean shouldDeferRemoval() {
         // TODO: This should probably return false if mChildren.isEmpty() regardless if the stack
         // is animating...
-        return hasWindowsAlive() && mStack.isAnimating();
+        return hasWindowsAlive() && mStack.isSelfOrChildAnimating();
     }
 
     @Override
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 28b1390..9946c6a 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -935,7 +935,7 @@
 
     @Override
     void removeIfPossible() {
-        if (isAnimating()) {
+        if (isSelfOrChildAnimating()) {
             mDeferRemoval = true;
             return;
         }
@@ -1643,7 +1643,7 @@
 
     /** Returns true if a removal action is still being deferred. */
     boolean checkCompleteDeferredRemoval() {
-        if (isAnimating()) {
+        if (isSelfOrChildAnimating()) {
             return true;
         }
         if (mDeferRemoval) {
diff --git a/services/core/java/com/android/server/wm/WallpaperWindowToken.java b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
index a12c0e5..3389f71 100644
--- a/services/core/java/com/android/server/wm/WallpaperWindowToken.java
+++ b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
@@ -115,7 +115,7 @@
     void startAnimation(Animation anim) {
         for (int ndx = mChildren.size() - 1; ndx >= 0; ndx--) {
             final WindowState windowState = mChildren.get(ndx);
-            windowState.mWinAnimator.setAnimation(anim);
+            windowState.startAnimation(anim);
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/WindowAnimationSpec.java b/services/core/java/com/android/server/wm/WindowAnimationSpec.java
index 56175c7..bb25297 100644
--- a/services/core/java/com/android/server/wm/WindowAnimationSpec.java
+++ b/services/core/java/com/android/server/wm/WindowAnimationSpec.java
@@ -31,7 +31,7 @@
 
     private Animation mAnimation;
     private final Point mPosition = new Point();
-    private final ThreadLocal<Tmp> mThreadLocalTmps = ThreadLocal.withInitial(Tmp::new);
+    private final ThreadLocal<TmpValues> mThreadLocalTmps = ThreadLocal.withInitial(TmpValues::new);
 
     public WindowAnimationSpec(Animation animation, Point position)  {
         mAnimation = animation;
@@ -55,7 +55,7 @@
 
     @Override
     public void apply(Transaction t, SurfaceControl leash, long currentPlayTime) {
-        final Tmp tmp = mThreadLocalTmps.get();
+        final TmpValues tmp = mThreadLocalTmps.get();
         tmp.transformation.clear();
         mAnimation.getTransformation(currentPlayTime, tmp.transformation);
         tmp.transformation.getMatrix().postTranslate(mPosition.x, mPosition.y);
@@ -63,7 +63,7 @@
         t.setAlpha(leash, tmp.transformation.getAlpha());
     }
 
-    private static class Tmp {
+    private static class TmpValues {
         final Transformation transformation = new Transformation();
         final float[] floats = new float[9];
     }
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index d6329bf..b2b6119 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -19,6 +19,9 @@
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 import static com.android.server.wm.proto.WindowContainerProto.CONFIGURATION_CONTAINER;
 import static com.android.server.wm.proto.WindowContainerProto.ORIENTATION;
 import static com.android.server.wm.proto.WindowContainerProto.VISIBLE;
@@ -26,14 +29,20 @@
 
 import android.annotation.CallSuper;
 import android.content.res.Configuration;
+import android.graphics.PixelFormat.Opacity;
+import android.util.Slog;
 import android.view.MagnificationSpec;
 import android.view.SurfaceControl;
+import android.view.SurfaceControl.Builder;
 import android.view.SurfaceSession;
 import android.util.Pools;
 
 import android.util.proto.ProtoOutputStream;
-import com.android.internal.util.ToBooleanFunction;
 
+import com.android.internal.util.ToBooleanFunction;
+import com.android.server.wm.SurfaceAnimator.Animatable;
+
+import java.io.PrintWriter;
 import java.util.Comparator;
 import java.util.LinkedList;
 import java.util.function.Consumer;
@@ -46,7 +55,9 @@
  * changes are made to this class.
  */
 class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<E>
-        implements Comparable<WindowContainer> {
+        implements Comparable<WindowContainer>, Animatable {
+
+    private static final String TAG = TAG_WITH_CLASS_NAME ? "WindowContainer" : TAG_WM;
 
     static final int POSITION_TOP = Integer.MAX_VALUE;
     static final int POSITION_BOTTOM = Integer.MIN_VALUE;
@@ -56,7 +67,7 @@
      * For removing or setting new parent {@link #setParent} should be used, because it also
      * performs configuration updates based on new parent's settings.
      */
-    private WindowContainer mParent = null;
+    private WindowContainer<WindowContainer> mParent = null;
 
     // List of children for this window container. List is in z-order as the children appear on
     // screen with the top-most window container at the tail of the list.
@@ -69,7 +80,7 @@
             new Pools.SynchronizedPool<>(3);
 
     // The owner/creator for this container. No controller if null.
-     WindowContainerController mController;
+    WindowContainerController mController;
 
     protected SurfaceControl mSurfaceControl;
     private int mLastLayer = 0;
@@ -78,12 +89,14 @@
     /**
      * Applied as part of the animation pass in "prepareSurfaces".
      */
-    private final Transaction mPendingTransaction;
+    protected final Transaction mPendingTransaction;
+    protected final SurfaceAnimator mSurfaceAnimator;
     protected final WindowManagerService mService;
 
     WindowContainer(WindowManagerService service) {
         mService = service;
         mPendingTransaction = service.mTransactionFactory.make();
+        mSurfaceAnimator = new SurfaceAnimator(this, this::onAnimationFinished, service);
     }
 
     @Override
@@ -101,7 +114,7 @@
         return mChildren.get(index);
     }
 
-    final protected void setParent(WindowContainer parent) {
+    final protected void setParent(WindowContainer<WindowContainer> parent) {
         mParent = parent;
         // Removing parent usually means that we've detached this entity to destroy it or to attach
         // to another parent. In both cases we don't need to update the configuration now.
@@ -123,14 +136,18 @@
         if (mParent == null) {
             return;
         }
+
         if (mSurfaceControl == null) {
             // If we don't yet have a surface, but we now have a parent, we should
             // build a surface.
             mSurfaceControl = makeSurface().build();
             getPendingTransaction().show(mSurfaceControl);
         } else {
-            // If we have a surface but a new parent, we just need to perform a reparent.
-            getPendingTransaction().reparent(mSurfaceControl, mParent.mSurfaceControl.getHandle());
+            // If we have a surface but a new parent, we just need to perform a reparent. Go through
+            // surface animator such that hierarchy is preserved when animating, i.e.
+            // mSurfaceControl stays attached to the leash and we just reparent the leash to the
+            // new parent.
+            mSurfaceAnimator.reparent(getPendingTransaction(), mParent.mSurfaceControl);
         }
 
         // Either way we need to ask the parent to assign us a Z-order.
@@ -139,8 +156,8 @@
     }
 
     // Temp. holders for a chain of containers we are currently processing.
-    private final LinkedList<WindowContainer> mTmpChain1 = new LinkedList();
-    private final LinkedList<WindowContainer> mTmpChain2 = new LinkedList();
+    private final LinkedList<WindowContainer> mTmpChain1 = new LinkedList<>();
+    private final LinkedList<WindowContainer> mTmpChain2 = new LinkedList<>();
 
     /**
      * Adds the input window container has a child of this container in order based on the input
@@ -214,7 +231,7 @@
     @CallSuper
     void removeImmediately() {
         while (!mChildren.isEmpty()) {
-            final WindowContainer child = mChildren.peekLast();
+            final E child = mChildren.peekLast();
             child.removeImmediately();
             // Need to do this after calling remove on the child because the child might try to
             // remove/detach itself from its parent which will cause an exception if we remove
@@ -401,16 +418,40 @@
         }
     }
 
-    boolean isAnimating() {
+    /**
+     * @return Whether our own container is running an animation or any child, no matter how deep in
+     *         the hierarchy, is animating.
+     */
+    boolean isSelfOrChildAnimating() {
+        if (isSelfAnimating()) {
+            return true;
+        }
         for (int j = mChildren.size() - 1; j >= 0; j--) {
             final WindowContainer wc = mChildren.get(j);
-            if (wc.isAnimating()) {
+            if (wc.isSelfOrChildAnimating()) {
                 return true;
             }
         }
         return false;
     }
 
+    /**
+     * @return Whether our own container is running an animation or our parent is animating. This
+     *         doesn't consider whether children are animating.
+     */
+    boolean isAnimating() {
+
+        // We are animating if we ourselves are animating or if our parent is animating.
+        return isSelfAnimating() || mParent != null && mParent.isAnimating();
+    }
+
+    /**
+     * @return Whether our own container running an animation at the moment.
+     */
+    boolean isSelfAnimating() {
+        return mSurfaceAnimator.isAnimating();
+    }
+
     void sendAppVisibilityToClients() {
         for (int i = mChildren.size() - 1; i >= 0; --i) {
             final WindowContainer wc = mChildren.get(i);
@@ -743,6 +784,15 @@
                 .setParent(mSurfaceControl);
     }
 
+    @Override
+    public SurfaceControl getParentSurfaceControl() {
+        final WindowContainer parent = getParent();
+        if (parent == null) {
+            return null;
+        }
+        return parent.getSurfaceControl();
+    }
+
     /**
      * @return Whether this WindowContainer should be magnified by the accessibility magnifier.
      */
@@ -765,7 +815,10 @@
     void assignLayer(Transaction t, int layer) {
         final boolean changed = layer != mLastLayer || mLastRelativeToLayer != null;
         if (mSurfaceControl != null && changed) {
-            t.setLayer(mSurfaceControl, layer);
+
+            // Route through surface animator to accommodate that our surface control might be
+            // attached to the leash, and leash is attached to parent container.
+            mSurfaceAnimator.setLayer(t, layer);
             mLastLayer = layer;
             mLastRelativeToLayer = null;
         }
@@ -774,7 +827,10 @@
     void assignRelativeLayer(Transaction t, SurfaceControl relativeTo, int layer) {
         final boolean changed = layer != mLastLayer || mLastRelativeToLayer != relativeTo;
         if (mSurfaceControl != null && changed) {
-            t.setRelativeLayer(mSurfaceControl, relativeTo, layer);
+
+            // Route through surface animator to accommodate that our surface control might be
+            // attached to the leash, and leash is attached to parent container.
+            mSurfaceAnimator.setRelativeLayer(t, relativeTo, layer);
             mLastLayer = layer;
             mLastRelativeToLayer = relativeTo;
         }
@@ -897,7 +953,8 @@
         }
     }
 
-    SurfaceControl getSurfaceControl() {
+    @Override
+    public SurfaceControl getSurfaceControl() {
         return mSurfaceControl;
     }
 
@@ -907,13 +964,105 @@
      * rather than an intentional design, so please take care when
      * expanding use.
      */
-    void destroyAfterPendingTransaction(SurfaceControl surface) {
+    @Override
+    public void destroyAfterPendingTransaction(SurfaceControl surface) {
         if (mParent != null) {
             mParent.destroyAfterPendingTransaction(surface);
         }
     }
-    
-    Transaction getPendingTransaction() {
+
+    @Override
+    public Transaction getPendingTransaction() {
         return mPendingTransaction;
     }
+
+    /**
+     * Starts an animation on the container.
+     *
+     * @param anim The animation to run.
+     * @param hidden Whether our container is currently hidden. TODO This should use isVisible at
+     *               some point but the meaning is too weird to work for all containers.
+     */
+    void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden) {
+        if (DEBUG_ANIM) Slog.v(TAG, "Starting animation on " + this + ": " + anim);
+
+        // TODO: This should use isVisible() but because isVisible has a really weird meaning at
+        // the moment this doesn't work for all animatable window containers.
+        mSurfaceAnimator.startAnimation(t, anim, hidden);
+    }
+
+    void cancelAnimation() {
+        mSurfaceAnimator.cancelAnimation();
+    }
+
+    @Override
+    public Builder makeAnimationLeash() {
+        return makeSurface();
+    }
+
+    @Override
+    public void commitPendingTransaction() {
+        scheduleAnimation();
+    }
+
+    private void reassignLayer(Transaction t) {
+        final WindowContainer parent = getParent();
+        if (parent != null) {
+            parent.assignChildLayers(t);
+        }
+    }
+
+    @Override
+    public void onAnimationLeashCreated(Transaction t, SurfaceControl leash) {
+        reassignLayer(t);
+    }
+
+    @Override
+    public void onAnimationLeashDestroyed(Transaction t) {
+        reassignLayer(t);
+    }
+
+    /**
+     * Called when an animation has finished running.
+     */
+    protected void onAnimationFinished() {
+    }
+
+    /**
+     * @return The currently running animation, if any, or {@code null} otherwise.
+     */
+    AnimationAdapter getAnimation() {
+        return mSurfaceAnimator.getAnimation();
+    }
+
+    /**
+     * @see SurfaceAnimator#startDelayingAnimationStart
+     */
+    void startDelayingAnimationStart() {
+        mSurfaceAnimator.startDelayingAnimationStart();
+    }
+
+    /**
+     * @see SurfaceAnimator#endDelayingAnimationStart
+     */
+    void endDelayingAnimationStart() {
+        mSurfaceAnimator.endDelayingAnimationStart();
+    }
+
+    @Override
+    public int getSurfaceWidth() {
+        return mSurfaceControl.getWidth();
+    }
+
+    @Override
+    public int getSurfaceHeight() {
+        return mSurfaceControl.getHeight();
+    }
+
+    void dump(PrintWriter pw, String prefix, boolean dumpAll) {
+        if (mSurfaceAnimator.isAnimating()) {
+            pw.print(prefix); pw.println("ContainerAnimator:");
+            mSurfaceAnimator.dump(pw, prefix + "  ");
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 8c9948e..7a3ba74 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -725,8 +725,6 @@
         }
     }
 
-    boolean mAnimateWallpaperWithTarget;
-
     // TODO: Move to RootWindowContainer
     AppWindowToken mFocusedApp = null;
 
@@ -2182,18 +2180,15 @@
         if (win.isWinVisibleLw() && winAnimator.applyAnimationLocked(transit, false)) {
             focusMayChange = isDefaultDisplay;
             win.mAnimatingExit = true;
-            win.mWinAnimator.mAnimating = true;
         } else if (win.mWinAnimator.isAnimationSet()) {
             // Currently in a hide animation... turn this into
             // an exit.
             win.mAnimatingExit = true;
-            win.mWinAnimator.mAnimating = true;
         } else if (win.getDisplayContent().mWallpaperController.isWallpaperTarget(win)) {
             // If the wallpaper is currently behind this
             // window, we need to change both of them inside
             // of a transaction to avoid artifacts.
             win.mAnimatingExit = true;
-            win.mWinAnimator.mAnimating = true;
         } else {
             if (mInputMethodWindow == win) {
                 setInputMethodWindowLocked(null);
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index e38605d..6bebcf4 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -19,11 +19,11 @@
 import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
 import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.SurfaceControl.Transaction;
 import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT;
 import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME;
 import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
 import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE;
-import static android.view.SurfaceControl.Transaction;
 import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
 import static android.view.WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW;
 import static android.view.WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON;
@@ -91,6 +91,7 @@
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 import static com.android.server.wm.WindowManagerService.H.SEND_NEW_CONFIGURATION;
+import static com.android.server.wm.WindowManagerService.MAX_ANIMATION_DURATION;
 import static com.android.server.wm.WindowManagerService.TYPE_LAYER_MULTIPLIER;
 import static com.android.server.wm.WindowManagerService.TYPE_LAYER_OFFSET;
 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
@@ -160,10 +161,14 @@
 import android.view.ViewTreeObserver;
 import android.view.WindowInfo;
 import android.view.WindowManager;
+import android.view.animation.Animation;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
 
 import com.android.internal.util.ToBooleanFunction;
 import com.android.server.input.InputWindowHandle;
 import com.android.server.policy.WindowManagerPolicy;
+import com.android.server.wm.LocalAnimationAdapter.AnimationSpec;
 
 import java.io.PrintWriter;
 import java.lang.ref.WeakReference;
@@ -594,6 +599,8 @@
      */
     private boolean mDrawnStateEvaluated;
 
+    private final Point mSurfacePosition = new Point();
+
     /**
      * Compares two window sub-layers and returns -1 if the first is lesser than the second in terms
      * of z-order and 1 otherwise.
@@ -1470,9 +1477,9 @@
         final AppWindowToken atoken = mAppToken;
         if (atoken != null) {
             return ((!isParentWindowHidden() && !atoken.hiddenRequested)
-                    || mWinAnimator.mAnimation != null || atoken.mAppAnimator.animation != null);
+                    || mWinAnimator.isAnimationSet() || atoken.mAppAnimator.animation != null);
         }
-        return !isParentWindowHidden() || mWinAnimator.mAnimation != null;
+        return !isParentWindowHidden() || mWinAnimator.isAnimationSet();
     }
 
     /**
@@ -1505,7 +1512,7 @@
         }
         return mHasSurface && mPolicyVisibility && !mDestroying
                 && ((!isParentWindowHidden() && mViewVisibility == View.VISIBLE && !mToken.hidden)
-                        || mWinAnimator.mAnimation != null
+                        || mWinAnimator.isAnimationSet()
                         || ((mAppToken != null) && (mAppToken.mAppAnimator.animation != null)));
     }
 
@@ -1520,8 +1527,7 @@
         // started.
         final boolean appAnimationStarting = mAppToken != null
                 && mAppToken.mAppAnimator.isAnimationStarting();
-        final boolean exitingSelf = mAnimatingExit && (!mWinAnimator.isAnimationStarting()
-                && !appAnimationStarting);
+        final boolean exitingSelf = mAnimatingExit && !appAnimationStarting;
         final boolean appExiting = mAppToken != null && mAppToken.hidden && !appAnimationStarting;
 
         final boolean exiting = exitingSelf || mDestroying || appExiting;
@@ -1544,7 +1550,7 @@
         return isDrawnLw() && mPolicyVisibility
             && ((!isParentWindowHidden() &&
                     (atoken == null || !atoken.hiddenRequested))
-                        || mWinAnimator.mAnimating
+                        || mWinAnimator.isAnimationSet()
                         || (atoken != null && atoken.mAppAnimator.animation != null));
     }
 
@@ -1553,7 +1559,7 @@
      */
     @Override
     public boolean isAnimatingLw() {
-        return mWinAnimator.mAnimation != null
+        return mWinAnimator.isAnimationSet()
                 || (mAppToken != null && mAppToken.mAppAnimator.animation != null);
     }
 
@@ -1600,7 +1606,7 @@
         // to determine if it's occluding apps.
         return ((!mIsWallpaper && mAttrs.format == PixelFormat.OPAQUE)
                 || (mIsWallpaper && mWallpaperVisible))
-                && isDrawnLw() && mWinAnimator.mAnimation == null
+                && isDrawnLw() && !mWinAnimator.isAnimationSet()
                 && (mAppToken == null || mAppToken.mAppAnimator.animation == null);
     }
 
@@ -1732,7 +1738,7 @@
      * listeners and optionally animate it. Simply checking a change of position is not enough,
      * because being move due to dock divider is not a trigger for animation.
      */
-    void handleWindowMovedIfNeeded() {
+    void handleWindowMovedIfNeeded(Transaction t) {
         if (!hasMoved()) {
             return;
         }
@@ -1750,7 +1756,7 @@
                 && !isDragResizing() && !adjustedForMinimizedDockOrIme
                 && getWindowConfiguration().hasMovementAnimations()
                 && !mWinAnimator.mLastHidden) {
-            mWinAnimator.setMoveAnimation(left, top);
+            startMoveAnimation(t, left, top);
         }
 
         //TODO (multidisplay): Accessibility supported only for the default display.
@@ -2457,10 +2463,10 @@
         if (DEBUG_VISIBILITY) Slog.v(TAG, "Policy visibility true: " + this);
         if (doAnimation) {
             if (DEBUG_VISIBILITY) Slog.v(TAG, "doAnimation: mPolicyVisibility="
-                    + mPolicyVisibility + " mAnimation=" + mWinAnimator.mAnimation);
+                    + mPolicyVisibility + " isAnimationSet=" + mWinAnimator.isAnimationSet());
             if (!mToken.okToAnimate()) {
                 doAnimation = false;
-            } else if (mPolicyVisibility && mWinAnimator.mAnimation == null) {
+            } else if (mPolicyVisibility && !mWinAnimator.isAnimationSet()) {
                 // Check for the case where we are currently visible and
                 // not animating; we do not want to do animation at such a
                 // point to become visible when we already are.
@@ -2499,7 +2505,7 @@
         }
         if (doAnimation) {
             mWinAnimator.applyAnimationLocked(TRANSIT_EXIT, false);
-            if (mWinAnimator.mAnimation == null) {
+            if (!mWinAnimator.isAnimationSet()) {
                 doAnimation = false;
             }
         }
@@ -2599,14 +2605,6 @@
         return mAnimatingExit || (mService.mClosingApps.contains(mAppToken));
     }
 
-    @Override
-    boolean isAnimating() {
-        if (mWinAnimator.isAnimationSet() || mAnimatingExit) {
-            return true;
-        }
-        return super.isAnimating();
-    }
-
     void addWinAnimatorToList(ArrayList<WindowStateAnimator> animators) {
         animators.add(mWinAnimator);
 
@@ -3133,6 +3131,7 @@
         proto.end(token);
     }
 
+    @Override
     void dump(PrintWriter pw, String prefix, boolean dumpAll) {
         final TaskStack stack = getStack();
         pw.print(prefix); pw.print("mDisplayId="); pw.print(getDisplayId());
@@ -3275,6 +3274,7 @@
                     pw.print(" cutout=" + mLastDisplayCutout);
                     pw.println();
         }
+        super.dump(pw, prefix, dumpAll);
         pw.print(prefix); pw.print(mWinAnimator); pw.println(":");
         mWinAnimator.dump(pw, prefix + "  ", dumpAll);
         if (mAnimatingExit || mRemoveOnExit || mDestroying || mRemoved) {
@@ -3333,7 +3333,7 @@
     @Override
     String getName() {
         return Integer.toHexString(System.identityHashCode(this))
-            + " " + getWindowTag();
+                + " " + getWindowTag();
     }
 
     CharSequence getWindowTag() {
@@ -3694,7 +3694,7 @@
                     + " tok.hiddenRequested="
                     + (mAppToken != null && mAppToken.hiddenRequested)
                     + " tok.hidden=" + (mAppToken != null && mAppToken.hidden)
-                    + " animating=" + mWinAnimator.mAnimating
+                    + " animationSet=" + mWinAnimator.isAnimationSet()
                     + " tok animating="
                     + (mWinAnimator.mAppAnimator != null && mWinAnimator.mAppAnimator.animating)
                     + " Callers=" + Debug.getCallers(4));
@@ -3900,23 +3900,10 @@
         return null;
     }
 
-    boolean isWindowAnimationSet() {
-        if (mWinAnimator.isWindowAnimationSet()) {
-            return true;
-        }
-        for (int i = mChildren.size() - 1; i >= 0; --i) {
-            final WindowState c = mChildren.get(i);
-            if (c.isWindowAnimationSet()) {
-                return true;
-            }
-        }
-        return false;
-    }
-
     void onExitAnimationDone() {
         if (DEBUG_ANIM) Slog.v(TAG, "onExitAnimationDone in " + this
                 + ": exiting=" + mAnimatingExit + " remove=" + mRemoveOnExit
-                + " windowAnimating=" + mWinAnimator.isWindowAnimationSet());
+                + " selfAnimating=" + isSelfAnimating());
 
         if (!mChildren.isEmpty()) {
             // Copying to a different list as multiple children can be removed.
@@ -3940,18 +3927,16 @@
             }
         }
 
-        if (!mWinAnimator.isWindowAnimationSet()) {
-            //TODO (multidisplay): Accessibility is supported only for the default display.
-            if (mService.mAccessibilityController != null && getDisplayId() == DEFAULT_DISPLAY) {
-                mService.mAccessibilityController.onSomeWindowResizedOrMovedLocked();
-            }
-        }
-
-        if (!mAnimatingExit) {
+        if (isSelfAnimating()) {
             return;
         }
 
-        if (mWinAnimator.isWindowAnimationSet()) {
+        //TODO (multidisplay): Accessibility is supported only for the default display.
+        if (mService.mAccessibilityController != null && getDisplayId() == DEFAULT_DISPLAY) {
+            mService.mAccessibilityController.onSomeWindowResizedOrMovedLocked();
+        }
+
+        if (!mAnimatingExit) {
             return;
         }
 
@@ -4005,10 +3990,6 @@
                 mAnimatingExit = false;
                 didSomething = true;
             }
-            if (mWinAnimator.mAnimating) {
-                mWinAnimator.mAnimating = false;
-                didSomething = true;
-            }
             if (mDestroying) {
                 mDestroying = false;
                 mService.mDestroySurface.remove(this);
@@ -4097,7 +4078,7 @@
                         + " mDrawState=" + mWinAnimator.mDrawState
                         + " ph=" + isParentWindowHidden()
                         + " th=" + (mAppToken != null ? mAppToken.hiddenRequested : false)
-                        + " a=" + mWinAnimator.mAnimating);
+                        + " a=" + mWinAnimator.isAnimationSet());
             }
         }
 
@@ -4300,6 +4281,39 @@
         mLastDisplayCutout = mDisplayCutout;
     }
 
+    void startAnimation(Animation anim) {
+        final DisplayInfo displayInfo = getDisplayContent().getDisplayInfo();
+        anim.initialize(mFrame.width(), mFrame.height(),
+                displayInfo.appWidth, displayInfo.appHeight);
+        anim.restrictDuration(MAX_ANIMATION_DURATION);
+        anim.scaleCurrentDuration(mService.getWindowAnimationScaleLocked());
+        final AnimationAdapter adapter = new LocalAnimationAdapter(
+                new WindowAnimationSpec(anim, mSurfacePosition), mService.mSurfaceAnimationRunner);
+        startAnimation(mPendingTransaction, adapter);
+        commitPendingTransaction();
+    }
+
+    private void startMoveAnimation(Transaction t, int left, int top) {
+        if (DEBUG_ANIM) Slog.v(TAG, "Setting move animation on " + this);
+        final Point oldPosition = new Point();
+        final Point newPosition = new Point();
+        transformFrameToSurfacePosition(mLastFrame.left, mLastFrame.top, oldPosition);
+        transformFrameToSurfacePosition(left, top, newPosition);
+        final AnimationAdapter adapter = new LocalAnimationAdapter(
+                new MoveAnimationSpec(oldPosition.x, oldPosition.y, newPosition.x, newPosition.y),
+                mService.mSurfaceAnimationRunner);
+        startAnimation(t, adapter);
+    }
+
+    private void startAnimation(Transaction t, AnimationAdapter adapter) {
+        startAnimation(t, adapter, mWinAnimator.mLastHidden);
+    }
+
+    @Override
+    protected void onAnimationFinished() {
+        mWinAnimator.onAnimationFinished();
+    }
+
     // TODO: Hack to work around the number of states AppWindowToken needs to access without having
     // access to its windows children. Need to investigate re-writing
     // {@link AppWindowToken#updateReportedVisibilityLocked} so this can be removed.
@@ -4381,11 +4395,6 @@
         return getAnimLayerAdjustment() > 0 || mWillReplaceWindow;
     }
 
-    @Override
-    SurfaceControl.Builder makeSurface() {
-        return getParent().makeChildSurface(this);
-    }
-
     private void applyDims(Dimmer dimmer) {
         if (!mAnimatingExit && mAppDied) {
             mIsDimming = true;
@@ -4405,11 +4414,51 @@
             applyDims(dimmer);
         }
 
+        updateSurfacePosition(mPendingTransaction);
+
         mWinAnimator.prepareSurfaceLocked(true);
         super.prepareSurfaces();
     }
 
     @Override
+    public void onAnimationLeashCreated(Transaction t, SurfaceControl leash) {
+        super.onAnimationLeashCreated(t, leash);
+
+        // Leash is now responsible for position, so set our position to 0.
+        t.setPosition(mSurfaceControl, 0, 0);
+    }
+
+    @Override
+    public void onAnimationLeashDestroyed(Transaction t) {
+        super.onAnimationLeashDestroyed(t);
+        updateSurfacePosition(t);
+    }
+
+    void updateSurfacePosition(Transaction t) {
+        if (mSurfaceControl == null) {
+            return;
+        }
+
+        transformFrameToSurfacePosition(mFrame.left, mFrame.top, mSurfacePosition);
+        if (!mSurfaceAnimator.hasLeash()) {
+            t.setPosition(mSurfaceControl, mSurfacePosition.x, mSurfacePosition.y);
+        }
+    }
+
+    private void transformFrameToSurfacePosition(int left, int top, Point outPoint) {
+        outPoint.set(left, top);
+        if (isChildWindow()) {
+            // TODO: This probably falls apart at some point and we should
+            // actually compute relative coordinates.
+            final WindowState parent = getParentWindow();
+            outPoint.offset(-parent.mFrame.left, -parent.mFrame.top);
+        }
+
+        // Expand for surface insets. See WindowState.expandForSurfaceInsets.
+        outPoint.offset(-mAttrs.surfaceInsets.left, -mAttrs.surfaceInsets.top);
+    }
+
+    @Override
     void assignLayer(Transaction t, int layer) {
         // See comment in assignRelativeLayerForImeTargetChild
         if (!isChildWindow()
@@ -4449,4 +4498,34 @@
             layer++;
         }
     }
+
+    private final class MoveAnimationSpec implements AnimationSpec {
+
+        private final long mDuration;
+        private Interpolator mInterpolator;
+        private Point mFrom = new Point();
+        private Point mTo = new Point();
+
+        private MoveAnimationSpec(int fromX, int fromY, int toX, int toY) {
+            final Animation anim = AnimationUtils.loadAnimation(mContext,
+                    com.android.internal.R.anim.window_move_from_decor);
+            mDuration = anim.computeDurationHint();
+            mInterpolator = anim.getInterpolator();
+            mFrom.set(fromX, fromY);
+            mTo.set(toX, toY);
+        }
+
+        @Override
+        public long getDuration() {
+            return mDuration;
+        }
+
+        @Override
+        public void apply(Transaction t, SurfaceControl leash, long currentPlayTime) {
+            final float fraction = (float) currentPlayTime / getDuration();
+            final float v = mInterpolator.getInterpolation(fraction);
+            t.setPosition(leash, mFrom.x + (mTo.x - mFrom.x) * v,
+                    mFrom.y + (mTo.y - mFrom.y) * v);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 54c84ea..0eabc89 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -23,16 +23,16 @@
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
+import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
+import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
 import static com.android.server.wm.AppWindowAnimator.sDummyAnimation;
 import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_FREEFORM;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYERS;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW_VERBOSE;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_CROP;
 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS;
 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_SURFACE_ALLOC;
@@ -40,7 +40,6 @@
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 import static com.android.server.wm.WindowManagerService.TYPE_LAYER_MULTIPLIER;
-import static com.android.server.wm.WindowManagerService.localLOGV;
 import static com.android.server.wm.WindowManagerService.logWithStack;
 import static com.android.server.wm.WindowSurfacePlacer.SET_ORIENTATION_CHANGE_COMPLETE;
 import static com.android.server.wm.WindowSurfacePlacer.SET_TURN_ON_SCREEN;
@@ -65,14 +64,13 @@
 import android.view.WindowManager;
 import android.view.WindowManager.LayoutParams;
 import android.view.animation.Animation;
-import android.view.animation.AnimationSet;
 import android.view.animation.AnimationUtils;
 import android.view.animation.Transformation;
 
 import com.android.server.policy.WindowManagerPolicy;
 
-import java.io.PrintWriter;
 import java.io.FileDescriptor;
+import java.io.PrintWriter;
 
 /**
  * Keep track of animations and surface operations for a single WindowState.
@@ -112,20 +110,9 @@
     final boolean mIsWallpaper;
     private final WallpaperController mWallpaperControllerLocked;
 
-    // Currently running animation.
-    boolean mAnimating;
-    boolean mLocalAnimating;
-    Animation mAnimation;
     boolean mAnimationIsEntrance;
-    boolean mHasTransformation;
-    boolean mHasLocalTransformation;
-    final Transformation mTransformation = new Transformation();
-    boolean mWasAnimating;      // Were we animating going into the most recent animation step?
     int mAnimLayer;
     int mLastLayer;
-    long mAnimationStartTime;
-    long mLastAnimationTime;
-    int mStackClip = STACK_CLIP_BEFORE_ANIM;
 
     /**
      * Set when we have changed the size of the surface, to know that
@@ -168,13 +155,6 @@
     private final Rect mSystemDecorRect = new Rect();
     private final Rect mLastSystemDecorRect = new Rect();
 
-    // Used to save animation distances between the time they are calculated and when they are used.
-    private int mAnimDx;
-    private int mAnimDy;
-
-    /** Is the next animation to be started a window move animation? */
-    private boolean mAnimateMove = false;
-
     float mDsDx=1, mDtDx=0, mDsDy=0, mDtDy=1;
     private float mLastDsDx=1, mLastDtDx=0, mLastDsDy=0, mLastDtDy=1;
 
@@ -226,8 +206,6 @@
 
     int mAttrType;
 
-    static final long PENDING_TRANSACTION_FINISH_WAIT_TIME = 100;
-
     boolean mForceScaleUntilResize;
 
     // WindowState.mHScale and WindowState.mVScale contain the
@@ -247,15 +225,6 @@
         mAnimator = service.mAnimator;
         mPolicy = service.mPolicy;
         mContext = service.mContext;
-        final DisplayContent displayContent = win.getDisplayContent();
-        if (displayContent != null) {
-            final DisplayInfo displayInfo = displayContent.getDisplayInfo();
-            mAnimDx = displayInfo.appWidth;
-            mAnimDy = displayInfo.appHeight;
-        } else {
-            Slog.w(TAG, "WindowStateAnimator ctor: Display has been removed");
-            // This is checked on return and dealt with.
-        }
 
         mWin = win;
         mParentWinAnimator = !win.isChildWindow() ? null : win.getParentWindow().mWinAnimator;
@@ -266,54 +235,11 @@
         mWallpaperControllerLocked = mService.mRoot.mWallpaperController;
     }
 
-    public void setAnimation(Animation anim, long startTime, int stackClip) {
-        if (localLOGV) Slog.v(TAG, "Setting animation in " + this + ": " + anim);
-        mAnimating = false;
-        mLocalAnimating = false;
-        mAnimation = anim;
-        mAnimation.restrictDuration(WindowManagerService.MAX_ANIMATION_DURATION);
-        mAnimation.scaleCurrentDuration(mService.getWindowAnimationScaleLocked());
-        // Start out animation gone if window is gone, or visible if window is visible.
-        mTransformation.clear();
-        mTransformation.setAlpha(mLastHidden ? 0 : 1);
-        mHasLocalTransformation = true;
-        mAnimationStartTime = startTime;
-        mStackClip = stackClip;
-    }
-
-    public void setAnimation(Animation anim, int stackClip) {
-        setAnimation(anim, -1, stackClip);
-    }
-
-    public void setAnimation(Animation anim) {
-        setAnimation(anim, -1, STACK_CLIP_AFTER_ANIM);
-    }
-
-    public void clearAnimation() {
-        if (mAnimation != null) {
-            mAnimating = true;
-            mLocalAnimating = false;
-            mAnimation.cancel();
-            mAnimation = null;
-            mStackClip = STACK_CLIP_BEFORE_ANIM;
-        }
-    }
-
     /**
      * Is the window or its container currently set to animate or currently animating?
      */
     boolean isAnimationSet() {
-        return mAnimation != null
-                || (mParentWinAnimator != null && mParentWinAnimator.mAnimation != null)
-                || (mAppAnimator != null && mAppAnimator.isAnimating());
-    }
-
-    /**
-     * @return whether an animation is about to start, i.e. the animation is set already but we
-     *         haven't processed the first frame yet.
-     */
-    boolean isAnimationStarting() {
-        return isAnimationSet() && !mAnimating;
+        return mWin.isAnimating();
     }
 
     /** Is the window animating the DummyAnimation? */
@@ -323,13 +249,6 @@
     }
 
     /**
-     * Is this window currently set to animate or currently animating?
-     */
-    boolean isWindowAnimationSet() {
-        return mAnimation != null;
-    }
-
-    /**
      * Is this window currently waiting to run an opening animation?
      */
     boolean isWaitingForOpening() {
@@ -341,130 +260,23 @@
         if (DEBUG_ANIM) Slog.d(TAG,
                 "cancelExitAnimationForNextAnimationLocked: " + mWin);
 
-        if (mAnimation != null) {
-            mAnimation.cancel();
-            mAnimation = null;
-            mLocalAnimating = false;
-            mWin.destroySurfaceUnchecked();
-        }
+        mWin.cancelAnimation();
+        mWin.destroySurfaceUnchecked();
     }
 
-    private boolean stepAnimation(long currentTime) {
-        if ((mAnimation == null) || !mLocalAnimating) {
-            return false;
-        }
-        currentTime = getAnimationFrameTime(mAnimation, currentTime);
-        mTransformation.clear();
-        final boolean more = mAnimation.getTransformation(currentTime, mTransformation);
-        if (mAnimationStartDelayed && mAnimationIsEntrance) {
-            mTransformation.setAlpha(0f);
-        }
-        if (false && DEBUG_ANIM) Slog.v(TAG, "Stepped animation in " + this + ": more=" + more
-                + ", xform=" + mTransformation);
-        return more;
-    }
-
-    // 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 isAnimationSet().
-        mWasAnimating = mAnimating;
-        final DisplayContent displayContent = mWin.getDisplayContent();
-        if (mWin.mToken.okToAnimate()) {
-            // We will run animations as long as the display isn't frozen.
-
-            if (mWin.isDrawnLw() && mAnimation != null) {
-                mHasTransformation = true;
-                mHasLocalTransformation = true;
-                if (!mLocalAnimating) {
-                    if (DEBUG_ANIM) Slog.v(
-                        TAG, "Starting animation in " + this +
-                        " @ " + currentTime + ": ww=" + mWin.mFrame.width() +
-                        " wh=" + mWin.mFrame.height() +
-                        " dx=" + mAnimDx + " dy=" + mAnimDy +
-                        " scale=" + mService.getWindowAnimationScaleLocked());
-                    final DisplayInfo displayInfo = displayContent.getDisplayInfo();
-                    if (mAnimateMove) {
-                        mAnimateMove = false;
-                        mAnimation.initialize(mWin.mFrame.width(), mWin.mFrame.height(),
-                                mAnimDx, mAnimDy);
-                    } else {
-                        mAnimation.initialize(mWin.mFrame.width(), mWin.mFrame.height(),
-                                displayInfo.appWidth, displayInfo.appHeight);
-                    }
-                    mAnimDx = displayInfo.appWidth;
-                    mAnimDy = displayInfo.appHeight;
-                    mAnimation.setStartTime(mAnimationStartTime != -1
-                            ? mAnimationStartTime
-                            : currentTime);
-                    mLocalAnimating = true;
-                    mAnimating = true;
-                }
-                if ((mAnimation != null) && mLocalAnimating) {
-                    mLastAnimationTime = currentTime;
-                    if (stepAnimation(currentTime)) {
-                        return true;
-                    }
-                }
-                if (DEBUG_ANIM) Slog.v(
-                    TAG, "Finished animation in " + this +
-                    " @ " + currentTime);
-                //WindowManagerService.this.dump();
-            }
-            mHasLocalTransformation = false;
-            if ((!mLocalAnimating || mAnimationIsEntrance) && mAppAnimator != null
-                    && mAppAnimator.animation != null) {
-                // When our app token is animating, we kind-of pretend like
-                // we are as well.  Note the mLocalAnimating mAnimationIsEntrance
-                // part of this check means that we will only do this if
-                // our window is not currently exiting, or it is not
-                // locally animating itself.  The idea being that one that
-                // is exiting and doing a local animation should be removed
-                // once that animation is done.
-                mAnimating = true;
-                mHasTransformation = true;
-                mTransformation.clear();
-                return false;
-            } else if (mHasTransformation) {
-                // Little trick to get through the path below to act like
-                // we have finished an animation.
-                mAnimating = true;
-            } else if (isAnimationSet()) {
-                mAnimating = true;
-            }
-        } else if (mAnimation != null) {
-            // If the display is frozen, and there is a pending animation,
-            // clear it and make sure we run the cleanup code.
-            mAnimating = true;
-        }
-
-        if (!mAnimating && !mLocalAnimating) {
-            return false;
-        }
-
+    void onAnimationFinished() {
         // Done animating, clean up.
         if (DEBUG_ANIM) Slog.v(
-            TAG, "Animation done in " + this + ": exiting=" + mWin.mAnimatingExit
-            + ", reportedVisible="
-            + (mWin.mAppToken != null ? mWin.mAppToken.reportedVisible : false));
+                TAG, "Animation done in " + this + ": exiting=" + mWin.mAnimatingExit
+                        + ", reportedVisible="
+                        + (mWin.mAppToken != null ? mWin.mAppToken.reportedVisible : false));
 
-        mAnimating = false;
-        mLocalAnimating = false;
-        if (mAnimation != null) {
-            mAnimation.cancel();
-            mAnimation = null;
-        }
         if (mAnimator.mWindowDetachedWallpaper == mWin) {
             mAnimator.mWindowDetachedWallpaper = null;
         }
-        mAnimLayer = mWin.getSpecialWindowAnimLayerAdjustment();
-        if (DEBUG_LAYERS) Slog.v(TAG, "Stepping win " + this + " anim layer: " + mAnimLayer);
-        mHasTransformation = false;
-        mHasLocalTransformation = false;
-        mStackClip = STACK_CLIP_BEFORE_ANIM;
+
         mWin.checkPolicyVisibilityChange();
-        mTransformation.clear();
+        final DisplayContent displayContent = mWin.getDisplayContent();
         if (mAttrType == LayoutParams.TYPE_STATUS_BAR && mWin.mPolicyVisibility) {
             // Upon completion of a not-visible to visible status bar animation a relayout is
             // required.
@@ -475,7 +287,11 @@
 
         mWin.onExitAnimationDone();
         final int displayId = mWin.getDisplayId();
-        mAnimator.setPendingLayoutChanges(displayId, WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM);
+        int pendingLayoutChanges = FINISH_LAYOUT_REDO_ANIM;
+        if (displayContent.mWallpaperController.isWallpaperTarget(mWin)) {
+            pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
+        }
+        mAnimator.setPendingLayoutChanges(displayId, pendingLayoutChanges);
         if (DEBUG_LAYOUT_REPEATS)
             mService.mWindowPlacerLocked.debugLayoutRepeats(
                     "WindowStateAnimator", mAnimator.getPendingLayoutChanges(displayId));
@@ -483,8 +299,6 @@
         if (mWin.mAppToken != null) {
             mWin.mAppToken.updateReportedVisibilityLocked();
         }
-
-        return false;
     }
 
     void hide(String reason) {
@@ -683,8 +497,8 @@
             }
 
             mSurfaceController = new WindowSurfaceController(mSession.mSurfaceSession,
-                    attrs.getTitle().toString(),
-                    width, height, format, flags, this, windowType, ownerUid);
+                    attrs.getTitle().toString(), width, height, format, flags, this,
+                    windowType, ownerUid);
             mSurfaceFormat = format;
 
             w.setHasSurface(true);
@@ -853,35 +667,8 @@
     }
 
     void computeShownFrameLocked() {
-        final boolean selfTransformation = mHasLocalTransformation;
         Transformation appTransformation = (mAppAnimator != null && mAppAnimator.hasTransformation)
                 ? mAppAnimator.transformation : null;
-        Transformation wallpaperTargetTransformation = null;
-
-        // Wallpapers are animated based on the "real" window they
-        // are currently targeting.
-        final WindowState wallpaperTarget = mWallpaperControllerLocked.getWallpaperTarget();
-        if (mIsWallpaper && wallpaperTarget != null && mService.mAnimateWallpaperWithTarget) {
-            final WindowStateAnimator wallpaperAnimator = wallpaperTarget.mWinAnimator;
-            if (wallpaperAnimator.mHasLocalTransformation &&
-                    wallpaperAnimator.mAnimation != null &&
-                    !wallpaperAnimator.mAnimation.getDetachWallpaper()) {
-                wallpaperTargetTransformation = wallpaperAnimator.mTransformation;
-                if (DEBUG_WALLPAPER && wallpaperTargetTransformation != null) {
-                    Slog.v(TAG, "WP target attached xform: " + wallpaperTargetTransformation);
-                }
-            }
-            final AppWindowAnimator wpAppAnimator = wallpaperTarget.mAppToken == null ?
-                    null : wallpaperTarget.mAppToken.mAppAnimator;
-                if (wpAppAnimator != null && wpAppAnimator.hasTransformation
-                    && wpAppAnimator.animation != null
-                    && !wpAppAnimator.animation.getDetachWallpaper()) {
-                appTransformation = wpAppAnimator.transformation;
-                if (DEBUG_WALLPAPER && appTransformation != null) {
-                    Slog.v(TAG, "WP target app xform: " + appTransformation);
-                }
-            }
-        }
 
         final int displayId = mWin.getDisplayId();
         final ScreenRotationAnimation screenRotationAnimation =
@@ -890,8 +677,7 @@
                 screenRotationAnimation != null && screenRotationAnimation.isAnimating();
 
         mHasClipRect = false;
-        if (selfTransformation || wallpaperTargetTransformation != null
-                || appTransformation != null || screenAnimation) {
+        if (appTransformation != null || screenAnimation) {
             // cache often used attributes locally
             final Rect frame = mWin.mFrame;
             final float tmpFloats[] = mService.mTmpFloats;
@@ -917,34 +703,16 @@
                 tmpMatrix.reset();
             }
             tmpMatrix.postScale(mWin.mGlobalScale, mWin.mGlobalScale);
-            if (selfTransformation) {
-                tmpMatrix.postConcat(mTransformation.getMatrix());
-            }
 
-            if (wallpaperTargetTransformation != null) {
-                tmpMatrix.postConcat(wallpaperTargetTransformation.getMatrix());
-            }
+            // WindowState.prepareSurfaces expands for surface insets (in order they don't get
+            // clipped by the WindowState surface), so we need to go into the other direction here.
+            tmpMatrix.postTranslate(mWin.mXOffset + mWin.mAttrs.surfaceInsets.left,
+                    mWin.mYOffset + mWin.mAttrs.surfaceInsets.top);
+
             if (appTransformation != null) {
                 tmpMatrix.postConcat(appTransformation.getMatrix());
             }
 
-            int left = frame.left;
-            int top = frame.top;
-            if (mWin.isChildWindow()) {
-                WindowState parent = mWin.getParentWindow();
-                left -= parent.mFrame.left;
-                top  -= parent.mFrame.top;
-            }
-
-            // The translation that applies the position of the window needs to be applied at the
-            // end in case that other translations include scaling. Otherwise the scaling will
-            // affect this translation. But it needs to be set before the screen rotation animation
-            // so the pivot point is at the center of the screen for all windows.
-            tmpMatrix.postTranslate(left + mWin.mXOffset, top + mWin.mYOffset);
-            if (screenAnimation) {
-                tmpMatrix.postConcat(screenRotationAnimation.getEnterTransformation().getMatrix());
-            }
-
             // "convert" it into SurfaceFlinger's format
             // (a 2x2 matrix + an offset)
             // Here we must not transform the position of the surface
@@ -972,12 +740,6 @@
                     || (mWin.isIdentityMatrix(mDsDx, mDtDx, mDtDy, mDsDy)
                             && x == frame.left && y == frame.top))) {
                 //Slog.i(TAG_WM, "Applying alpha transform");
-                if (selfTransformation) {
-                    mShownAlpha *= mTransformation.getAlpha();
-                }
-                if (wallpaperTargetTransformation != null) {
-                    mShownAlpha *= wallpaperTargetTransformation.getAlpha();
-                }
                 if (appTransformation != null) {
                     mShownAlpha *= appTransformation.getAlpha();
                     if (appTransformation.hasClipRect()) {
@@ -1006,9 +768,6 @@
             if ((DEBUG_ANIM || WindowManagerService.localLOGV)
                     && (mShownAlpha == 1.0 || mShownAlpha == 0.0)) Slog.v(
                     TAG, "computeShownFrameLocked: Animating " + this + " mAlpha=" + mAlpha
-                    + " self=" + (selfTransformation ? mTransformation.getAlpha() : "null")
-                    + " attached=" + (wallpaperTargetTransformation == null ?
-                            "null" : wallpaperTargetTransformation.getAlpha())
                     + " app=" + (appTransformation == null ? "null" : appTransformation.getAlpha())
                     + " screen=" + (screenAnimation ?
                             screenRotationAnimation.getEnterTransformation().getAlpha() : "null"));
@@ -1028,10 +787,10 @@
                 TAG, "computeShownFrameLocked: " + this +
                 " not attached, mAlpha=" + mAlpha);
 
-        mWin.mShownPosition.set(mWin.mFrame.left, mWin.mFrame.top);
-        if (mWin.mXOffset != 0 || mWin.mYOffset != 0) {
-            mWin.mShownPosition.offset(mWin.mXOffset, mWin.mYOffset);
-        }
+        // WindowState.prepareSurfaces expands for surface insets (in order they don't get
+        // clipped by the WindowState surface), so we need to go into the other direction here.
+        mWin.mShownPosition.set(mWin.mXOffset + mWin.mAttrs.surfaceInsets.left,
+                mWin.mYOffset + mWin.mAttrs.surfaceInsets.top);
         mShownAlpha = mAlpha;
         mHaveMatrix = false;
         mDsDx = mWin.mGlobalScale;
@@ -1182,7 +941,7 @@
         if (mAppAnimator != null && mAppAnimator.animation != null) {
             return mAppAnimator.getStackClip();
         } else {
-            return mStackClip;
+            return STACK_CLIP_AFTER_ANIM;
         }
     }
 
@@ -1434,7 +1193,7 @@
         if (mSurfaceResized) {
             mReportSurfaceResized = true;
             mAnimator.setPendingLayoutChanges(w.getDisplayId(),
-                    WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER);
+                    FINISH_LAYOUT_REDO_WALLPAPER);
         }
     }
 
@@ -1546,7 +1305,7 @@
                         // Run another pass through performLayout to set mHasContent in the
                         // LogicalDisplay.
                         mAnimator.setPendingLayoutChanges(w.getDisplayId(),
-                                WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM);
+                                FINISH_LAYOUT_REDO_ANIM);
                     } else {
                         w.setOrientationChanging(false);
                     }
@@ -1720,12 +1479,18 @@
      * @return true if an animation has been loaded.
      */
     boolean applyAnimationLocked(int transit, boolean isEntrance) {
-        if (mLocalAnimating && mAnimationIsEntrance == isEntrance) {
+        if (mWin.isSelfAnimating() && mAnimationIsEntrance == isEntrance) {
             // If we are trying to apply an animation, but already running
             // an animation of the same type, then just leave that one alone.
             return true;
         }
 
+        if (isEntrance && mWin.mAttrs.type == TYPE_INPUT_METHOD) {
+            mWin.getDisplayContent().adjustForImeIfNeeded();
+            mWin.setDisplayLayoutNeeded();
+            mService.mWindowPlacerLocked.requestTraversal();
+        }
+
         // Only apply an animation if the display isn't frozen.  If it is
         // frozen, there is no reason to animate and it can cause strange
         // artifacts when we unfreeze the display if some different animation
@@ -1764,44 +1529,19 @@
                     + " isEntrance=" + isEntrance + " Callers " + Debug.getCallers(3));
             if (a != null) {
                 if (DEBUG_ANIM) logWithStack(TAG, "Loaded animation " + a + " for " + this);
-                setAnimation(a);
+                mWin.startAnimation(a);
                 mAnimationIsEntrance = isEntrance;
             }
         } else {
-            clearAnimation();
+            mWin.cancelAnimation();
         }
-        Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
 
-        if (mWin.mAttrs.type == TYPE_INPUT_METHOD) {
+        if (!isEntrance && mWin.mAttrs.type == TYPE_INPUT_METHOD) {
             mWin.getDisplayContent().adjustForImeIfNeeded();
-            if (isEntrance) {
-                mWin.setDisplayLayoutNeeded();
-                mService.mWindowPlacerLocked.requestTraversal();
-            }
         }
-        return mAnimation != null;
-    }
 
-    private void applyFadeoutDuringKeyguardExitAnimation() {
-        long startTime = mAnimation.getStartTime();
-        long duration = mAnimation.getDuration();
-        long elapsed = mLastAnimationTime - startTime;
-        long fadeDuration = duration - elapsed;
-        if (fadeDuration <= 0) {
-            // Never mind, this would be no visible animation, so abort the animation change.
-            return;
-        }
-        AnimationSet newAnimation = new AnimationSet(false /* shareInterpolator */);
-        newAnimation.setDuration(duration);
-        newAnimation.setStartTime(startTime);
-        newAnimation.addAnimation(mAnimation);
-        Animation fadeOut = AnimationUtils.loadAnimation(
-                mContext, com.android.internal.R.anim.app_starting_exit);
-        fadeOut.setDuration(fadeDuration);
-        fadeOut.setStartOffset(elapsed);
-        newAnimation.addAnimation(fadeOut);
-        newAnimation.initialize(mWin.mFrame.width(), mWin.mFrame.height(), mAnimDx, mAnimDy);
-        mAnimation = newAnimation;
+        Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
+        return isAnimationSet();
     }
 
     void writeToProto(ProtoOutputStream proto, long fieldId) {
@@ -1814,20 +1554,8 @@
     }
 
     public void dump(PrintWriter pw, String prefix, boolean dumpAll) {
-        if (mAnimating || mLocalAnimating || mAnimationIsEntrance
-                || mAnimation != null) {
-            pw.print(prefix); pw.print("mAnimating="); pw.print(mAnimating);
-                    pw.print(" mLocalAnimating="); pw.print(mLocalAnimating);
-                    pw.print(" mAnimationIsEntrance="); pw.print(mAnimationIsEntrance);
-                    pw.print(" mAnimation="); pw.print(mAnimation);
-                    pw.print(" mStackClip="); pw.println(mStackClip);
-        }
-        if (mHasTransformation || mHasLocalTransformation) {
-            pw.print(prefix); pw.print("XForm: has=");
-                    pw.print(mHasTransformation);
-                    pw.print(" hasLocal="); pw.print(mHasLocalTransformation);
-                    pw.print(" "); mTransformation.printShortString(pw);
-                    pw.println();
+        if (mAnimationIsEntrance) {
+            pw.print(prefix); pw.print(" mAnimationIsEntrance="); pw.print(mAnimationIsEntrance);
         }
         if (mSurfaceController != null) {
             mSurfaceController.dump(pw, prefix, dumpAll);
@@ -1907,44 +1635,6 @@
         }
     }
 
-    void setMoveAnimation(int left, int top) {
-        final Animation a = AnimationUtils.loadAnimation(mContext,
-                com.android.internal.R.anim.window_move_from_decor);
-        setAnimation(a);
-        mAnimDx = mWin.mLastFrame.left - left;
-        mAnimDy = mWin.mLastFrame.top - top;
-        mAnimateMove = true;
-    }
-
-    void deferTransactionUntilParentFrame(long frameNumber) {
-        if (!mWin.isChildWindow()) {
-            return;
-        }
-        mSurfaceController.deferTransactionUntil(
-                mWin.getParentWindow().mWinAnimator.mSurfaceController.getHandle(), frameNumber);
-    }
-
-    /**
-     * Sometimes we need to synchronize the first frame of animation with some external event.
-     * To achieve this, we prolong the start of the animation and keep producing the first frame of
-     * the animation.
-     */
-    private long getAnimationFrameTime(Animation animation, long currentTime) {
-        if (mAnimationStartDelayed) {
-            animation.setStartTime(currentTime);
-            return currentTime + 1;
-        }
-        return currentTime;
-    }
-
-    void startDelayingAnimationStart() {
-        mAnimationStartDelayed = true;
-    }
-
-    void endDelayingAnimationStart() {
-        mAnimationStartDelayed = false;
-    }
-
     void seamlesslyRotateWindow(int oldRotation, int newRotation) {
         final WindowState w = mWin;
         if (!w.isVisibleNow() || w.mIsWallpaper) {
@@ -2026,4 +1716,8 @@
             mSurfaceController.detachChildren();
         }
     }
+
+    int getLayer() {
+        return mLastLayer;
+    }
 }
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
index 6746754..e26a362 100644
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -59,8 +59,8 @@
     private boolean mSurfaceShown = false;
     private float mSurfaceX = 0;
     private float mSurfaceY = 0;
-    private float mSurfaceW = 0;
-    private float mSurfaceH = 0;
+    private int mSurfaceW = 0;
+    private int mSurfaceH = 0;
 
     // Initialize to the identity matrix.
     private float mLastDsdx = 1;
@@ -517,11 +517,11 @@
         return mSurfaceY;
     }
 
-    float getWidth() {
+    int getWidth() {
         return mSurfaceW;
     }
 
-    float getHeight() {
+    int getHeight() {
         return mSurfaceH;
     }
 
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 169d0a3..d8e7457 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -462,6 +462,7 @@
             appAnimator.setNullAnimation();
             // TODO: Do we need to add to mNoAnimationNotifyOnTransitionFinished like above if not
             //       animating?
+            wtoken.setAllAppWinAnimators();
             wtoken.setVisibility(animLp, false, transit, false, voiceInteraction);
             wtoken.updateReportedVisibilityLocked();
             // setAllAppWinAnimators so the windows get onExitAnimationDone once the animation is
@@ -606,7 +607,7 @@
                         + ", oldWallpaper=" + oldWallpaper
                         + ", openingApps=" + openingApps
                         + ", closingApps=" + closingApps);
-        mService.mAnimateWallpaperWithTarget = false;
+
         if (openingCanBeWallpaperTarget && transit == TRANSIT_KEYGUARD_GOING_AWAY) {
             transit = TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER;
             if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
@@ -645,8 +646,6 @@
                 transit = TRANSIT_WALLPAPER_OPEN;
                 if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "New transit into wallpaper: "
                         + AppTransition.appTransitionToString(transit));
-            } else {
-                mService.mAnimateWallpaperWithTarget = true;
             }
         }
         return transit;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
index 84cfabe..9163037 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
@@ -17,6 +17,7 @@
 
 import android.app.admin.IDevicePolicyManager;
 import android.content.ComponentName;
+import android.os.PersistableBundle;
 
 import com.android.internal.R;
 import com.android.server.SystemService;
@@ -57,4 +58,6 @@
     abstract void handleStopUser(int userId);
     
     public void setSystemSetting(ComponentName who, String setting, String value){}
+
+    public void transferOwner(ComponentName admin, ComponentName target, PersistableBundle bundle) {}
 }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 7ee047b..2080b72 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -751,7 +751,7 @@
         private static final String ATTR_NUM_NETWORK_LOGGING_NOTIFICATIONS = "num-notifications";
         private static final String TAG_IS_LOGOUT_ENABLED = "is_logout_enabled";
 
-        final DeviceAdminInfo info;
+        DeviceAdminInfo info;
 
 
         static final int DEF_PASSWORD_HISTORY_LENGTH = 0;
@@ -1402,6 +1402,13 @@
             return userRestrictions;
         }
 
+        public void transfer(DeviceAdminInfo deviceAdminInfo) {
+            if (hasParentActiveAdmin()) {
+                parentAdmin.info = deviceAdminInfo;
+            }
+            info = deviceAdminInfo;
+        }
+
         void dump(String prefix, PrintWriter pw) {
             pw.print(prefix); pw.print("uid="); pw.println(getUid());
             pw.print(prefix); pw.print("testOnlyAdmin=");
@@ -2541,7 +2548,7 @@
 
 
     public DeviceAdminInfo findAdmin(ComponentName adminName, int userHandle,
-            boolean throwForMissiongPermission) {
+            boolean throwForMissingPermission) {
         if (!mHasFeature) {
             return null;
         }
@@ -2564,7 +2571,7 @@
             final String message = "DeviceAdminReceiver " + adminName + " must be protected with "
                     + permission.BIND_DEVICE_ADMIN;
             Slog.w(LOG_TAG, message);
-            if (throwForMissiongPermission &&
+            if (throwForMissingPermission &&
                     ai.applicationInfo.targetSdkVersion > Build.VERSION_CODES.M) {
                 throw new IllegalArgumentException(message);
             }
@@ -2889,7 +2896,7 @@
                     try {
                         DeviceAdminInfo dai = findAdmin(
                                 ComponentName.unflattenFromString(name), userHandle,
-                                /* throwForMissionPermission= */ false);
+                                /* throwForMissingPermission= */ false);
                         if (VERBOSE_LOG
                                 && (UserHandle.getUserId(dai.getActivityInfo().applicationInfo.uid)
                                 != userHandle)) {
@@ -3293,19 +3300,9 @@
 
         DevicePolicyData policy = getUserData(userHandle);
         DeviceAdminInfo info = findAdmin(adminReceiver, userHandle,
-                /* throwForMissionPermission= */ true);
-        if (info == null) {
-            throw new IllegalArgumentException("Bad admin: " + adminReceiver);
-        }
-        if (!info.getActivityInfo().applicationInfo.isInternal()) {
-            throw new IllegalArgumentException("Only apps in internal storage can be active admin: "
-                    + adminReceiver);
-        }
-        if (info.getActivityInfo().applicationInfo.isInstantApp()) {
-            throw new IllegalArgumentException("Instant apps cannot be device admins: "
-                    + adminReceiver);
-        }
+                /* throwForMissingPermission= */ true);
         synchronized (this) {
+            checkActiveAdminPrecondition(adminReceiver, info, policy);
             long ident = mInjector.binderClearCallingIdentity();
             try {
                 final ActiveAdmin existingAdmin
@@ -3313,10 +3310,6 @@
                 if (!refreshing && existingAdmin != null) {
                     throw new IllegalArgumentException("Admin is already added");
                 }
-                if (policy.mRemovingAdmins.contains(adminReceiver)) {
-                    throw new IllegalArgumentException(
-                            "Trying to set an admin which is being removed");
-                }
                 ActiveAdmin newAdmin = new ActiveAdmin(info, /* parent */ false);
                 newAdmin.testOnlyAdmin =
                         (existingAdmin != null) ? existingAdmin.testOnlyAdmin
@@ -3346,6 +3339,46 @@
         }
     }
 
+    private void transferActiveAdminUncheckedLocked(ComponentName incomingReceiver,
+            ComponentName outgoingReceiver, int userHandle) {
+        final DevicePolicyData policy = getUserData(userHandle);
+        final DeviceAdminInfo incomingDeviceInfo = findAdmin(incomingReceiver, userHandle,
+            /* throwForMissingPermission= */ true);
+        final ActiveAdmin adminToTransfer = policy.mAdminMap.get(outgoingReceiver);
+        final int oldAdminUid = adminToTransfer.getUid();
+
+        adminToTransfer.transfer(incomingDeviceInfo);
+        policy.mAdminMap.remove(outgoingReceiver);
+        policy.mAdminMap.put(incomingReceiver, adminToTransfer);
+        if (policy.mPasswordOwner == oldAdminUid) {
+            policy.mPasswordOwner = adminToTransfer.getUid();
+        }
+
+        saveSettingsLocked(userHandle);
+        //TODO: Make sure we revert back when we detect a failure.
+        sendAdminCommandLocked(adminToTransfer, DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED,
+                null, null);
+    }
+
+    private void checkActiveAdminPrecondition(ComponentName adminReceiver, DeviceAdminInfo info,
+            DevicePolicyData policy) {
+        if (info == null) {
+            throw new IllegalArgumentException("Bad admin: " + adminReceiver);
+        }
+        if (!info.getActivityInfo().applicationInfo.isInternal()) {
+            throw new IllegalArgumentException("Only apps in internal storage can be active admin: "
+                    + adminReceiver);
+        }
+        if (info.getActivityInfo().applicationInfo.isInstantApp()) {
+            throw new IllegalArgumentException("Instant apps cannot be device admins: "
+                    + adminReceiver);
+        }
+        if (policy.mRemovingAdmins.contains(adminReceiver)) {
+            throw new IllegalArgumentException(
+                    "Trying to set an admin which is being removed");
+        }
+    }
+
     @Override
     public boolean isAdminActive(ComponentName adminReceiver, int userHandle) {
         if (!mHasFeature) {
@@ -11538,4 +11571,60 @@
         return new ArrayList<>(
                 mOverlayPackagesProvider.getNonRequiredApps(admin, userId, provisioningAction));
     }
+
+    //TODO: Add callback information to the javadoc once it is completed.
+    //TODO: Make transferOwner atomic.
+    @Override
+    public void transferOwner(ComponentName admin, ComponentName target, PersistableBundle bundle) {
+        if (!mHasFeature) {
+            return;
+        }
+
+        Preconditions.checkNotNull(admin, "Admin cannot be null.");
+        Preconditions.checkNotNull(target, "Target cannot be null.");
+
+        enforceProfileOrDeviceOwner(admin);
+
+        if (admin.equals(target)) {
+            throw new IllegalArgumentException("Provided administrator and target are "
+                    + "the same object.");
+        }
+
+        if (admin.getPackageName().equals(target.getPackageName())) {
+            throw new IllegalArgumentException("Provided administrator and target have "
+                    + "the same package name.");
+        }
+
+        final int callingUserId = mInjector.userHandleGetCallingUserId();
+        final DevicePolicyData policy = getUserData(callingUserId);
+        final DeviceAdminInfo incomingDeviceInfo = findAdmin(target, callingUserId,
+                /* throwForMissingPermission= */ true);
+        checkActiveAdminPrecondition(target, incomingDeviceInfo, policy);
+
+        final long id = mInjector.binderClearCallingIdentity();
+        try {
+            //STOPSHIP add support for COMP, DO, edge cases when device is rebooted/work mode off,
+            //transfer callbacks and broadcast
+            if (isProfileOwner(admin, callingUserId)) {
+                transferProfileOwner(admin, target, callingUserId);
+            }
+        } finally {
+            mInjector.binderRestoreCallingIdentity(id);
+        }
+    }
+
+    /**
+     * Transfers the profile owner for user with id profileOwnerUserId from admin to target.
+     */
+    private void transferProfileOwner(ComponentName admin, ComponentName target,
+            int profileOwnerUserId) {
+        synchronized (this) {
+            transferActiveAdminUncheckedLocked(target, admin, profileOwnerUserId);
+            mOwners.transferProfileOwner(target, profileOwnerUserId);
+            Slog.i(LOG_TAG, "Profile owner set: " + target + " on user " + profileOwnerUserId);
+            mOwners.writeProfileOwner(profileOwnerUserId);
+            mDeviceAdminServiceController.startServiceForOwner(
+                    target.getPackageName(), profileOwnerUserId, "transfer-profile-owner");
+        }
+    }
 }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
index a5500dd..9042a8d 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
@@ -277,6 +277,17 @@
         }
     }
 
+    void transferProfileOwner(ComponentName target, int userId) {
+        synchronized (mLock) {
+            final OwnerInfo ownerInfo = mProfileOwners.get(userId);
+            final OwnerInfo newOwnerInfo = new OwnerInfo(target.getPackageName(), target,
+                    ownerInfo.userRestrictionsMigrated, ownerInfo.remoteBugreportUri,
+                    ownerInfo.remoteBugreportHash);
+            mProfileOwners.put(userId, newOwnerInfo);
+            pushToPackageManagerLocked();
+        }
+    }
+
     ComponentName getProfileOwnerComponent(int userId) {
         synchronized (mLock) {
             OwnerInfo profileOwner = mProfileOwners.get(userId);
diff --git a/services/robotests/Android.mk b/services/robotests/Android.mk
index 114929c..b5e4af7 100644
--- a/services/robotests/Android.mk
+++ b/services/robotests/Android.mk
@@ -40,15 +40,15 @@
 
 # Include the testing libraries (JUnit4 + Robolectric libs).
 LOCAL_STATIC_JAVA_LIBRARIES := \
+    platform-robolectric-android-all-stubs \
     android-support-test \
     mockito-robolectric-prebuilt \
     platform-test-annotations \
     truth-prebuilt
 
-# TODO(b/69254249): Migrate to Robolectric 3.4.2
 LOCAL_JAVA_LIBRARIES := \
     junit \
-    platform-robolectric-3.1.1-prebuilt
+    platform-robolectric-3.5.1-prebuilt
 
 LOCAL_INSTRUMENTATION_FOR := FrameworksServicesLib
 LOCAL_MODULE := FrameworksServicesRoboTests
@@ -73,5 +73,4 @@
 
 LOCAL_INSTRUMENT_SOURCE_DIRS := $(dir $(LOCAL_PATH))backup/java
 
-# TODO(b/69254249): Migrate to Robolectric 3.4.2
-include prebuilts/misc/common/robolectric/3.1.1/run_robotests.mk
+include prebuilts/misc/common/robolectric/3.5.1/run_robotests.mk
diff --git a/services/robotests/src/com/android/server/backup/TransportManagerTest.java b/services/robotests/src/com/android/server/backup/TransportManagerTest.java
index 3d5851f..2cb4a69 100644
--- a/services/robotests/src/com/android/server/backup/TransportManagerTest.java
+++ b/services/robotests/src/com/android/server/backup/TransportManagerTest.java
@@ -19,6 +19,7 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.Mockito.mock;
+import static org.robolectric.shadow.api.Shadow.extract;
 
 import android.app.backup.BackupManager;
 import android.content.ComponentName;
@@ -31,24 +32,25 @@
 import android.platform.test.annotations.Presubmit;
 
 import com.android.server.backup.testing.BackupTransportStub;
-import com.android.server.backup.testing.DefaultPackageManagerWithQueryIntentServicesAsUser;
 import com.android.server.backup.testing.ShadowBackupTransportStub;
 import com.android.server.backup.testing.ShadowContextImplForBackup;
+import com.android.server.backup.testing.ShadowPackageManagerForBackup;
 import com.android.server.backup.testing.TransportBoundListenerStub;
 import com.android.server.backup.testing.TransportReadyCallbackStub;
 import com.android.server.backup.transport.TransportClient;
+import com.android.server.testing.FrameworkRobolectricTestRunner;
+import com.android.server.testing.SystemLoaderClasses;
 
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 import org.robolectric.annotation.Config;
-import org.robolectric.res.builder.RobolectricPackageManager;
 import org.robolectric.shadows.ShadowLog;
 import org.robolectric.shadows.ShadowLooper;
+import org.robolectric.shadows.ShadowPackageManager;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -56,15 +58,17 @@
 import java.util.HashSet;
 import java.util.List;
 
-@RunWith(RobolectricTestRunner.class)
+@RunWith(FrameworkRobolectricTestRunner.class)
 @Config(
         manifest = Config.NONE,
-        sdk = 23,
+        sdk = 26,
         shadows = {
                 ShadowContextImplForBackup.class,
-                ShadowBackupTransportStub.class
+                ShadowBackupTransportStub.class,
+                ShadowPackageManagerForBackup.class
         }
 )
+@SystemLoaderClasses({TransportManager.class})
 @Presubmit
 public class TransportManagerTest {
     private static final String PACKAGE_NAME = "some.package.name";
@@ -73,7 +77,7 @@
     private TransportInfo mTransport1;
     private TransportInfo mTransport2;
 
-    private RobolectricPackageManager mPackageManager;
+    private ShadowPackageManager mPackageManagerShadow;
 
     private final TransportBoundListenerStub mTransportBoundListenerStub =
             new TransportBoundListenerStub(true);
@@ -86,9 +90,10 @@
         MockitoAnnotations.initMocks(this);
 
         ShadowLog.stream = System.out;
-        mPackageManager = new DefaultPackageManagerWithQueryIntentServicesAsUser(
-                RuntimeEnvironment.getAppResourceLoader());
-        RuntimeEnvironment.setRobolectricPackageManager(mPackageManager);
+
+        mPackageManagerShadow =
+                (ShadowPackageManagerForBackup)
+                        extract(RuntimeEnvironment.application.getPackageManager());
 
         mTransport1 = new TransportInfo(PACKAGE_NAME, "transport1.name");
         mTransport2 = new TransportInfo(PACKAGE_NAME, "transport2.name");
@@ -532,6 +537,18 @@
         assertThat(transportName).isEqualTo("newName");
     }
 
+    @Test
+    public void isTransportRegistered_returnsCorrectly() throws Exception {
+        TransportManager transportManager =
+                createTransportManagerAndSetUpTransports(
+                        Collections.singletonList(mTransport1),
+                        Collections.singletonList(mTransport2),
+                        mTransport1.name);
+
+        assertThat(transportManager.isTransportRegistered(mTransport1.name)).isTrue();
+        assertThat(transportManager.isTransportRegistered(mTransport2.name)).isFalse();
+    }
+
     private void setUpPackageWithTransports(String packageName, List<TransportInfo> transports,
             int flags) throws Exception {
         PackageInfo packageInfo = new PackageInfo();
@@ -539,7 +556,7 @@
         packageInfo.applicationInfo = new ApplicationInfo();
         packageInfo.applicationInfo.privateFlags = flags;
 
-        mPackageManager.addPackage(packageInfo);
+        mPackageManagerShadow.addPackage(packageInfo);
 
         List<ResolveInfo> transportsInfo = new ArrayList<>();
         for (TransportInfo transport : transports) {
@@ -553,7 +570,7 @@
         Intent intent = new Intent(TransportManager.SERVICE_ACTION_TRANSPORT_HOST);
         intent.setPackage(packageName);
 
-        mPackageManager.addResolveInfoForIntent(intent, transportsInfo);
+        mPackageManagerShadow.addResolveInfoForIntent(intent, transportsInfo);
     }
 
     private TransportManager createTransportManagerAndSetUpTransports(
diff --git a/services/robotests/src/com/android/server/backup/testing/DefaultPackageManagerWithQueryIntentServicesAsUser.java b/services/robotests/src/com/android/server/backup/testing/ShadowPackageManagerForBackup.java
similarity index 69%
rename from services/robotests/src/com/android/server/backup/testing/DefaultPackageManagerWithQueryIntentServicesAsUser.java
rename to services/robotests/src/com/android/server/backup/testing/ShadowPackageManagerForBackup.java
index 5a0967b..b64b59d 100644
--- a/services/robotests/src/com/android/server/backup/testing/DefaultPackageManagerWithQueryIntentServicesAsUser.java
+++ b/services/robotests/src/com/android/server/backup/testing/ShadowPackageManagerForBackup.java
@@ -16,28 +16,22 @@
 
 package com.android.server.backup.testing;
 
+import android.app.ApplicationPackageManager;
 import android.content.Intent;
 import android.content.pm.ResolveInfo;
 
-import org.robolectric.res.ResourceLoader;
-import org.robolectric.res.builder.DefaultPackageManager;
+import org.robolectric.annotation.Implements;
+import org.robolectric.shadows.ShadowApplicationPackageManager;
 
 import java.util.List;
 
 /**
  * Implementation of PackageManager for Robolectric which handles queryIntentServicesAsUser().
  */
-public class DefaultPackageManagerWithQueryIntentServicesAsUser extends
-        DefaultPackageManager {
-
-    /* package */
-    public DefaultPackageManagerWithQueryIntentServicesAsUser(
-            ResourceLoader appResourceLoader) {
-        super(appResourceLoader);
-    }
-
+@Implements(value = ApplicationPackageManager.class, inheritImplementationMethods = true)
+public class ShadowPackageManagerForBackup extends ShadowApplicationPackageManager {
     @Override
     public List<ResolveInfo> queryIntentServicesAsUser(Intent intent, int flags, int userId) {
-        return super.queryIntentServices(intent, flags);
+        return queryIntentServices(intent, flags);
     }
 }
diff --git a/services/robotests/src/com/android/server/backup/transport/TransportClientTest.java b/services/robotests/src/com/android/server/backup/transport/TransportClientTest.java
index 4a3277f..4462d2a 100644
--- a/services/robotests/src/com/android/server/backup/transport/TransportClientTest.java
+++ b/services/robotests/src/com/android/server/backup/transport/TransportClientTest.java
@@ -17,6 +17,7 @@
 package com.android.server.backup.transport;
 
 import static com.android.server.backup.TransportManager.SERVICE_ACTION_TRANSPORT_HOST;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.any;
@@ -37,31 +38,34 @@
 import android.platform.test.annotations.Presubmit;
 
 import com.android.internal.backup.IBackupTransport;
+import com.android.server.backup.TransportManager;
+import com.android.server.testing.FrameworkRobolectricTestRunner;
+import com.android.server.testing.SystemLoaderClasses;
 
 import org.junit.Before;
+import org.junit.BeforeClass;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
 import org.robolectric.annotation.Config;
 import org.robolectric.shadows.ShadowLooper;
 
-@RunWith(RobolectricTestRunner.class)
-@Config(manifest = Config.NONE, sdk = 23)
+@RunWith(FrameworkRobolectricTestRunner.class)
+@Config(manifest = Config.NONE, sdk = 26)
+@SystemLoaderClasses({TransportManager.class, TransportClient.class})
 @Presubmit
 public class TransportClientTest {
     private static final String PACKAGE_NAME = "some.package.name";
-    private static final ComponentName TRANSPORT_COMPONENT =
-            new ComponentName(PACKAGE_NAME, PACKAGE_NAME + ".transport.Transport");
-    private static final String TRANSPORT_DIR_NAME = TRANSPORT_COMPONENT.toString();
 
     @Mock private Context mContext;
     @Mock private TransportConnectionListener mTransportConnectionListener;
     @Mock private TransportConnectionListener mTransportConnectionListener2;
     @Mock private IBackupTransport.Stub mIBackupTransport;
     private TransportClient mTransportClient;
+    private ComponentName mTransportComponent;
+    private String mTransportDirName;
     private Intent mBindIntent;
     private ShadowLooper mShadowLooper;
 
@@ -71,13 +75,16 @@
 
         Looper mainLooper = Looper.getMainLooper();
         mShadowLooper = shadowOf(mainLooper);
-        mBindIntent = new Intent(SERVICE_ACTION_TRANSPORT_HOST).setComponent(TRANSPORT_COMPONENT);
+        mTransportComponent =
+                new ComponentName(PACKAGE_NAME, PACKAGE_NAME + ".transport.Transport");
+        mTransportDirName = mTransportComponent.toString();
+        mBindIntent = new Intent(SERVICE_ACTION_TRANSPORT_HOST).setComponent(mTransportComponent);
         mTransportClient =
                 new TransportClient(
                         mContext,
                         mBindIntent,
-                        TRANSPORT_COMPONENT,
-                        TRANSPORT_DIR_NAME,
+                        mTransportComponent,
+                        mTransportDirName,
                         "1",
                         new Handler(mainLooper));
 
@@ -91,12 +98,12 @@
 
     @Test
     public void testGetTransportDirName_returnsTransportDirName() {
-        assertThat(mTransportClient.getTransportDirName()).isEqualTo(TRANSPORT_DIR_NAME);
+        assertThat(mTransportClient.getTransportDirName()).isEqualTo(mTransportDirName);
     }
 
     @Test
     public void testGetTransportComponent_returnsTransportComponent() {
-        assertThat(mTransportClient.getTransportComponent()).isEqualTo(TRANSPORT_COMPONENT);
+        assertThat(mTransportClient.getTransportComponent()).isEqualTo(mTransportComponent);
     }
 
     @Test
@@ -117,7 +124,7 @@
 
         // Simulate framework connecting
         ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext);
-        connection.onServiceConnected(TRANSPORT_COMPONENT, mIBackupTransport);
+        connection.onServiceConnected(mTransportComponent, mIBackupTransport);
 
         mShadowLooper.runToEndOfTasks();
         verify(mTransportConnectionListener)
@@ -132,7 +139,7 @@
 
         mTransportClient.connectAsync(mTransportConnectionListener2, "caller2");
 
-        connection.onServiceConnected(TRANSPORT_COMPONENT, mIBackupTransport);
+        connection.onServiceConnected(mTransportComponent, mIBackupTransport);
 
         mShadowLooper.runToEndOfTasks();
         verify(mTransportConnectionListener)
@@ -145,7 +152,7 @@
     public void testConnectAsync_whenAlreadyConnected_callsListener() throws Exception {
         mTransportClient.connectAsync(mTransportConnectionListener, "caller1");
         ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext);
-        connection.onServiceConnected(TRANSPORT_COMPONENT, mIBackupTransport);
+        connection.onServiceConnected(mTransportComponent, mIBackupTransport);
 
         mTransportClient.connectAsync(mTransportConnectionListener2, "caller2");
 
@@ -190,8 +197,8 @@
             throws Exception {
         mTransportClient.connectAsync(mTransportConnectionListener, "caller1");
         ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext);
-        connection.onServiceConnected(TRANSPORT_COMPONENT, mIBackupTransport);
-        connection.onServiceDisconnected(TRANSPORT_COMPONENT);
+        connection.onServiceConnected(mTransportComponent, mIBackupTransport);
+        connection.onServiceDisconnected(mTransportComponent);
 
         mTransportClient.connectAsync(mTransportConnectionListener2, "caller1");
 
@@ -204,9 +211,9 @@
             throws Exception {
         mTransportClient.connectAsync(mTransportConnectionListener, "caller1");
         ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext);
-        connection.onServiceConnected(TRANSPORT_COMPONENT, mIBackupTransport);
-        connection.onServiceDisconnected(TRANSPORT_COMPONENT);
-        connection.onServiceConnected(TRANSPORT_COMPONENT, mIBackupTransport);
+        connection.onServiceConnected(mTransportComponent, mIBackupTransport);
+        connection.onServiceDisconnected(mTransportComponent);
+        connection.onServiceConnected(mTransportComponent, mIBackupTransport);
 
         mTransportClient.connectAsync(mTransportConnectionListener2, "caller1");
 
@@ -221,7 +228,7 @@
         mTransportClient.connectAsync(mTransportListener, "caller");
 
         ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext);
-        connection.onBindingDied(TRANSPORT_COMPONENT);
+        connection.onBindingDied(mTransportComponent);
 
         mShadowLooper.runToEndOfTasks();
         verify(mTransportListener).onTransportBound(isNull(), eq(mTransportClient));
@@ -235,7 +242,7 @@
 
         mTransportClient.connectAsync(mTransportListener2, "caller2");
 
-        connection.onBindingDied(TRANSPORT_COMPONENT);
+        connection.onBindingDied(mTransportComponent);
 
         mShadowLooper.runToEndOfTasks();
         verify(mTransportListener).onTransportBound(isNull(), eq(mTransportClient));
diff --git a/services/robotests/src/com/android/server/testing/FrameworkRobolectricTestRunner.java b/services/robotests/src/com/android/server/testing/FrameworkRobolectricTestRunner.java
new file mode 100644
index 0000000..78ac4ed
--- /dev/null
+++ b/services/robotests/src/com/android/server/testing/FrameworkRobolectricTestRunner.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2017 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.testing;
+
+import com.google.common.collect.ImmutableSet;
+
+import org.junit.runners.model.FrameworkMethod;
+import org.junit.runners.model.InitializationError;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.internal.SandboxFactory;
+import org.robolectric.internal.SdkEnvironment;
+import org.robolectric.internal.bytecode.InstrumentationConfiguration;
+import org.robolectric.internal.bytecode.SandboxClassLoader;
+import org.robolectric.util.Util;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.net.URL;
+import java.util.Arrays;
+import java.util.Set;
+
+import javax.annotation.Nonnull;
+
+/**
+ * HACK
+ * Robolectric loads up Android environment from prebuilt android jars before running a method.
+ * These jars are versioned according to the SDK level configured for the method (or class). The
+ * jars represent a snapshot of the Android APIs in that SDK level. For Robolectric tests that are
+ * testing Android components themselves we don't want certain classes (usually the
+ * class-under-test) to be loaded from the prebuilt jar, we want it instead to be loaded from the
+ * dependencies of our test target, i.e. the system class loader. That way we can write tests
+ * against the actual classes that are in the tree, not a past version of them. Ideally we would
+ * have a locally built jar referenced by Robolectric, but until that happens one can use this
+ * class.
+ * This class reads the {@link SystemLoaderClasses} annotation on test classes and for each class
+ * in that annotation value it will bypass the android jar and load it from the system class loader.
+ * Allowing the test to test the actual class in the tree.
+ *
+ * Implementation note: One could think about overriding
+ * {@link RobolectricTestRunner#createClassLoaderConfig(FrameworkMethod)} method and putting the
+ * classes in the annotation in the {@link InstrumentationConfiguration} list of classes not to
+ * acquire. Unfortunately, this will not work because we will not be instrumenting the class.
+ * Instead, we have to load the class bytes from the system class loader but still instrument it, we
+ * do this by overriding {@link SandboxClassLoader#getByteCode(String)} and loading the class bytes
+ * from the system class loader if it in the {@link SystemLoaderClasses} annotation. This way the
+ * {@link SandboxClassLoader} still instruments the class, but it's not loaded from the android jar.
+ * Finally, we inject the custom class loader in place of the default one.
+ *
+ * TODO: Remove this when we are using locally built android jars in the method's environment.
+ */
+public class FrameworkRobolectricTestRunner extends RobolectricTestRunner {
+    private final SandboxFactory mSandboxFactory;
+
+    public FrameworkRobolectricTestRunner(Class<?> testClass) throws InitializationError {
+        super(testClass);
+        SystemLoaderClasses annotation = testClass.getAnnotation(SystemLoaderClasses.class);
+        Class<?>[] systemLoaderClasses =
+                (annotation != null) ? annotation.value() : new Class<?>[0];
+        Set<String> systemLoaderClassNames = classesToClassNames(systemLoaderClasses);
+        mSandboxFactory = new FrameworkSandboxFactory(systemLoaderClassNames);
+    }
+
+    @Nonnull
+    @Override
+    protected SdkEnvironment getSandbox(FrameworkMethod method) {
+        // HACK: Calling super just to get SdkConfig via sandbox.getSdkConfig(), because
+        // RobolectricFrameworkMethod, the runtime class of method, is package-protected
+        SdkEnvironment sandbox = super.getSandbox(method);
+        return mSandboxFactory.getSdkEnvironment(
+                createClassLoaderConfig(method),
+                getJarResolver(),
+                sandbox.getSdkConfig());
+    }
+
+    private static class FrameworkClassLoader extends SandboxClassLoader {
+        private final Set<String> mSystemLoaderClasses;
+
+        private FrameworkClassLoader(
+                Set<String> systemLoaderClasses,
+                ClassLoader systemClassLoader,
+                InstrumentationConfiguration instrumentationConfig,
+                URL... urls) {
+            super(systemClassLoader, instrumentationConfig, urls);
+            mSystemLoaderClasses = systemLoaderClasses;
+        }
+
+        @Override
+        protected byte[] getByteCode(String className) throws ClassNotFoundException {
+            String classFileName = className.replace('.', '/') + ".class";
+            if (shouldLoadFromSystemLoader(className)) {
+                try (InputStream classByteStream = getResourceAsStream(classFileName)) {
+                    if (classByteStream == null) {
+                        throw new ClassNotFoundException(className);
+                    }
+                    return Util.readBytes(classByteStream);
+                } catch (IOException e) {
+                    throw new ClassNotFoundException(
+                            "Couldn't load " + className + " from system class loader", e);
+                }
+            }
+            return super.getByteCode(className);
+        }
+
+        /**
+         * Classes like com.package.ClassName$InnerClass should also be loaded from the system class
+         * loader, so we test if the classes in the annotation are prefixes of the class to load.
+         */
+        private boolean shouldLoadFromSystemLoader(String className) {
+            for (String classNamePrefix : mSystemLoaderClasses) {
+                if (className.startsWith(classNamePrefix)) {
+                    return true;
+                }
+            }
+            return false;
+        }
+    }
+
+    private static class FrameworkSandboxFactory extends SandboxFactory {
+        private final Set<String> mSystemLoaderClasses;
+
+        private FrameworkSandboxFactory(Set<String> systemLoaderClasses) {
+            mSystemLoaderClasses = systemLoaderClasses;
+        }
+
+        @Nonnull
+        @Override
+        public ClassLoader createClassLoader(
+                InstrumentationConfiguration instrumentationConfig, URL... urls) {
+            return new FrameworkClassLoader(
+                    mSystemLoaderClasses,
+                    ClassLoader.getSystemClassLoader(),
+                    instrumentationConfig,
+                    urls);
+        }
+    }
+
+    private static Set<String> classesToClassNames(Class<?>[] classes) {
+        ImmutableSet.Builder<String> builder = ImmutableSet.builder();
+        for (Class<?> classObject : classes) {
+            builder.add(classObject.getName());
+        }
+        return builder.build();
+    }
+}
diff --git a/services/robotests/src/com/android/server/testing/SystemLoaderClasses.java b/services/robotests/src/com/android/server/testing/SystemLoaderClasses.java
new file mode 100644
index 0000000..646a413
--- /dev/null
+++ b/services/robotests/src/com/android/server/testing/SystemLoaderClasses.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2017 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.testing;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation to be used in test classes that run with {@link FrameworkRobolectricTestRunner}.
+ * This will make the classes specified be loaded from the system class loader, NOT from the
+ * Robolectric android jar.
+ *
+ * @see FrameworkRobolectricTestRunner
+ */
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface SystemLoaderClasses {
+    Class<?>[] value() default {};
+}
diff --git a/services/tests/servicestests/src/com/android/server/wm/DimmerTests.java b/services/tests/servicestests/src/com/android/server/wm/DimmerTests.java
index 4dd51eb..70906df 100644
--- a/services/tests/servicestests/src/com/android/server/wm/DimmerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/DimmerTests.java
@@ -53,12 +53,12 @@
         }
 
         @Override
-        SurfaceControl getSurfaceControl() {
+        public SurfaceControl getSurfaceControl() {
             return mControl;
         }
 
         @Override
-        SurfaceControl.Transaction getPendingTransaction() {
+        public SurfaceControl.Transaction getPendingTransaction() {
             return mTransaction;
         }
     }
@@ -93,12 +93,12 @@
         }
 
         @Override
-        SurfaceControl getSurfaceControl() {
+        public SurfaceControl getSurfaceControl() {
             return mHostControl;
         }
 
         @Override
-        SurfaceControl.Transaction getPendingTransaction() {
+        public SurfaceControl.Transaction getPendingTransaction() {
             return mHostTransaction;
         }
     }
@@ -110,8 +110,8 @@
     @Before
     public void setUp() throws Exception {
         super.setUp();
-
         mHost = new MockSurfaceBuildingContainer();
+
         mTransaction = mock(SurfaceControl.Transaction.class);
         mDimmer = new Dimmer(mHost);
     }
diff --git a/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java b/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java
index 9ecf51e..c309611 100644
--- a/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java
@@ -24,9 +24,13 @@
 import static org.mockito.Mockito.atLeast;
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
+import android.animation.AnimationHandler;
 import android.animation.AnimationHandler.AnimationFrameCallbackProvider;
+import android.animation.ValueAnimator;
 import android.graphics.Matrix;
 import android.graphics.Point;
 import android.platform.test.annotations.Presubmit;
@@ -40,6 +44,7 @@
 import android.view.animation.TranslateAnimation;
 
 import com.android.server.wm.LocalAnimationAdapter.AnimationSpec;
+import com.android.server.wm.SurfaceAnimationRunner.AnimatorFactory;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -54,7 +59,7 @@
 /**
  * Test class for {@link SurfaceAnimationRunner}.
  *
- * runtest frameworks-services -c com.android.server.wm.SurfaceAnimationRunnerTest
+ * atest FrameworksServicesTests:com.android.server.wm.SurfaceAnimationRunnerTest
  */
 @SmallTest
 @Presubmit
@@ -63,6 +68,7 @@
 
     @Mock SurfaceControl mMockSurface;
     @Mock Transaction mMockTransaction;
+    @Mock AnimationSpec mMockAnimationSpec;
     @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
 
     private SurfaceAnimationRunner mSurfaceAnimationRunner;
@@ -72,7 +78,7 @@
     public void setUp() throws Exception {
         super.setUp();
         mFinishCallbackLatch = new CountDownLatch(1);
-        mSurfaceAnimationRunner = new SurfaceAnimationRunner(null /* callbackProvider */,
+        mSurfaceAnimationRunner = new SurfaceAnimationRunner(null /* callbackProvider */, null,
                 mMockTransaction);
     }
 
@@ -104,7 +110,8 @@
 
     @Test
     public void testCancel_notStarted() throws Exception {
-        mSurfaceAnimationRunner = new SurfaceAnimationRunner(new NoOpFrameCallbackProvider(), mMockTransaction);
+        mSurfaceAnimationRunner = new SurfaceAnimationRunner(new NoOpFrameCallbackProvider(), null,
+                mMockTransaction);
         mSurfaceAnimationRunner
                 .startAnimation(createTranslateAnimation(), mMockSurface, mMockTransaction,
                 this::finishedCallback);
@@ -112,22 +119,47 @@
         waitUntilHandlersIdle();
         assertTrue(mSurfaceAnimationRunner.mPendingAnimations.isEmpty());
         assertFinishCallbackNotCalled();
-        //verify(mMockSurface).release();
     }
 
     @Test
     public void testCancel_running() throws Exception {
-        mSurfaceAnimationRunner = new SurfaceAnimationRunner(new NoOpFrameCallbackProvider(), mMockTransaction);
-        mSurfaceAnimationRunner
-                .startAnimation(createTranslateAnimation(), mMockSurface, mMockTransaction,
-                this::finishedCallback);
+        mSurfaceAnimationRunner = new SurfaceAnimationRunner(new NoOpFrameCallbackProvider(), null,
+                mMockTransaction);
+        mSurfaceAnimationRunner.startAnimation(createTranslateAnimation(), mMockSurface,
+                mMockTransaction, this::finishedCallback);
         waitUntilNextFrame();
         assertFalse(mSurfaceAnimationRunner.mRunningAnimations.isEmpty());
         mSurfaceAnimationRunner.onAnimationCancelled(mMockSurface);
         assertTrue(mSurfaceAnimationRunner.mRunningAnimations.isEmpty());
         waitUntilHandlersIdle();
         assertFinishCallbackNotCalled();
-        //verify(mMockSurface).release();
+    }
+
+    @Test
+    public void testCancel_sneakyCancelBeforeUpdate() throws Exception {
+        mSurfaceAnimationRunner = new SurfaceAnimationRunner(null, () -> new ValueAnimator() {
+            {
+                setFloatValues(0f, 1f);
+            }
+
+            @Override
+            public void addUpdateListener(AnimatorUpdateListener listener) {
+                super.addUpdateListener(animation -> {
+                    // Sneaky test cancels animation just before applying frame to simulate
+                    // interleaving of multiple threads. Muahahaha
+                    if (animation.getCurrentPlayTime() > 0) {
+                        mSurfaceAnimationRunner.onAnimationCancelled(mMockSurface);
+                    }
+                    listener.onAnimationUpdate(animation);
+                });
+            }
+        }, mMockTransaction);
+        when(mMockAnimationSpec.getDuration()).thenReturn(200L);
+        mSurfaceAnimationRunner.startAnimation(mMockAnimationSpec, mMockSurface, mMockTransaction,
+                this::finishedCallback);
+        waitUntilNextFrame();
+        assertFalse(mSurfaceAnimationRunner.mRunningAnimations.isEmpty());
+        verify(mMockAnimationSpec, atLeastOnce()).apply(any(), any(), eq(0L));
     }
 
     private void waitUntilNextFrame() throws Exception {
diff --git a/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java b/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java
index 9a52042..6f739ca 100644
--- a/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java
@@ -33,8 +33,6 @@
 import android.view.SurfaceControl.Transaction;
 import android.view.SurfaceSession;
 
-import com.google.android.collect.Lists;
-
 import com.android.server.wm.SurfaceAnimator.Animatable;
 import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
 
@@ -50,27 +48,127 @@
 /**
  * Test class for {@link SurfaceAnimatorTest}.
  *
- * runtest frameworks-services -c com.android.server.wm.SurfaceAnimatorTest
+ * atest FrameworksServicesTests:com.android.server.wm.SurfaceAnimatorTest
  */
 @SmallTest
 @Presubmit
 @RunWith(AndroidJUnit4.class)
 public class SurfaceAnimatorTest extends WindowTestsBase {
 
-    @Mock
-    AnimationAdapter mSpec;
-    @Mock
-    AnimationAdapter mSpec2;
+    @Mock AnimationAdapter mSpec;
+    @Mock AnimationAdapter mSpec2;
     @Mock Transaction mTransaction;
 
-    private SurfaceAnimator mSurfaceAnimator;
-    private SurfaceControl mParent;
-    private SurfaceControl mSurface;
-    private boolean mFinishedCallbackCalled;
-    private SurfaceControl mLeash;
     private SurfaceSession mSession = new SurfaceSession();
+    private MyAnimatable mAnimatable;
 
-    private final Animatable mAnimatable = new Animatable() {
+    @Before
+    public void setUp() throws Exception {
+        super.setUp();
+        MockitoAnnotations.initMocks(this);
+        mAnimatable = new MyAnimatable();
+    }
+
+    @Test
+    public void testRunAnimation() throws Exception {
+        mAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
+        final ArgumentCaptor<OnAnimationFinishedCallback> callbackCaptor = ArgumentCaptor.forClass(
+                OnAnimationFinishedCallback.class);
+
+        assertTrue(mAnimatable.mSurfaceAnimator.isAnimating());
+        assertNotNull(mAnimatable.mSurfaceAnimator.getAnimation());
+        verify(mTransaction).reparent(eq(mAnimatable.mSurface), eq(mAnimatable.mLeash.getHandle()));
+        verify(mSpec).startAnimation(any(), any(), callbackCaptor.capture());
+
+        callbackCaptor.getValue().onAnimationFinished(mSpec);
+        assertFalse(mAnimatable.mSurfaceAnimator.isAnimating());
+        assertNull(mAnimatable.mSurfaceAnimator.getAnimation());
+        assertTrue(mAnimatable.mFinishedCallbackCalled);
+        assertTrue(mAnimatable.mPendingDestroySurfaces.contains(mAnimatable.mLeash));
+        // TODO: Verify reparenting once we use mPendingTransaction to reparent it back
+    }
+
+    @Test
+    public void testOverrideAnimation() throws Exception {
+        mAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
+        final SurfaceControl firstLeash = mAnimatable.mLeash;
+        mAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec2, true /* hidden */);
+
+        assertTrue(mAnimatable.mPendingDestroySurfaces.contains(firstLeash));
+        assertFalse(mAnimatable.mFinishedCallbackCalled);
+
+        final ArgumentCaptor<OnAnimationFinishedCallback> callbackCaptor = ArgumentCaptor.forClass(
+                OnAnimationFinishedCallback.class);
+        assertTrue(mAnimatable.mSurfaceAnimator.isAnimating());
+        assertNotNull(mAnimatable.mSurfaceAnimator.getAnimation());
+        verify(mSpec).startAnimation(any(), any(), callbackCaptor.capture());
+
+        // First animation was finished, but this shouldn't cancel the second animation
+        callbackCaptor.getValue().onAnimationFinished(mSpec);
+        assertTrue(mAnimatable.mSurfaceAnimator.isAnimating());
+
+        // Second animation was finished
+        verify(mSpec2).startAnimation(any(), any(), callbackCaptor.capture());
+        callbackCaptor.getValue().onAnimationFinished(mSpec2);
+        assertFalse(mAnimatable.mSurfaceAnimator.isAnimating());
+        assertTrue(mAnimatable.mFinishedCallbackCalled);
+    }
+
+    @Test
+    public void testCancelAnimation() throws Exception {
+        mAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
+        assertTrue(mAnimatable.mSurfaceAnimator.isAnimating());
+        mAnimatable.mSurfaceAnimator.cancelAnimation();
+        assertFalse(mAnimatable.mSurfaceAnimator.isAnimating());
+        verify(mSpec).onAnimationCancelled(any());
+        assertTrue(mAnimatable.mFinishedCallbackCalled);
+        assertTrue(mAnimatable.mPendingDestroySurfaces.contains(mAnimatable.mLeash));
+    }
+
+    @Test
+    public void testDelayingAnimationStart() throws Exception {
+        mAnimatable.mSurfaceAnimator.startDelayingAnimationStart();
+        mAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
+        verifyZeroInteractions(mSpec);
+        assertTrue(mAnimatable.mSurfaceAnimator.isAnimating());
+        mAnimatable.mSurfaceAnimator.endDelayingAnimationStart();
+        verify(mSpec).startAnimation(any(), any(), any());
+    }
+
+    @Test
+    public void testDelayingAnimationStartAndCancelled() throws Exception {
+        mAnimatable.mSurfaceAnimator.startDelayingAnimationStart();
+        mAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
+        mAnimatable.mSurfaceAnimator.cancelAnimation();
+        verifyZeroInteractions(mSpec);
+        assertFalse(mAnimatable.mSurfaceAnimator.isAnimating());
+        assertTrue(mAnimatable.mFinishedCallbackCalled);
+        assertTrue(mAnimatable.mPendingDestroySurfaces.contains(mAnimatable.mLeash));
+    }
+
+    private class MyAnimatable implements Animatable {
+
+        final SurfaceControl mParent;
+        final SurfaceControl mSurface;
+        final ArrayList<SurfaceControl> mPendingDestroySurfaces = new ArrayList<>();
+        final SurfaceAnimator mSurfaceAnimator;
+        SurfaceControl mLeash;
+        boolean mFinishedCallbackCalled;
+
+        MyAnimatable() {
+            mParent = sWm.makeSurfaceBuilder(mSession)
+                    .setName("test surface parent")
+                    .setSize(3000, 3000)
+                    .build();
+            mSurface = sWm.makeSurfaceBuilder(mSession)
+                    .setName("test surface")
+                    .setSize(1, 1)
+                    .build();
+            mFinishedCallbackCalled = false;
+            mLeash = null;
+            mSurfaceAnimator = new SurfaceAnimator(this, mFinishedCallback, sWm);
+        }
+
         @Override
         public Transaction getPendingTransaction() {
             return mTransaction;
@@ -81,15 +179,20 @@
         }
 
         @Override
-        public void onLeashCreated(Transaction t, SurfaceControl leash) {
+        public void onAnimationLeashCreated(Transaction t, SurfaceControl leash) {
         }
 
         @Override
-        public void onLeashDestroyed(Transaction t) {
+        public void onAnimationLeashDestroyed(Transaction t) {
         }
 
         @Override
-        public Builder makeLeash() {
+        public void destroyAfterPendingTransaction(SurfaceControl surface) {
+            mPendingDestroySurfaces.add(surface);
+        }
+
+        @Override
+        public Builder makeAnimationLeash() {
             return new SurfaceControl.Builder(mSession) {
 
                 @Override
@@ -101,12 +204,12 @@
         }
 
         @Override
-        public SurfaceControl getSurface() {
+        public SurfaceControl getSurfaceControl() {
             return mSurface;
         }
 
         @Override
-        public SurfaceControl getParentSurface() {
+        public SurfaceControl getParentSurfaceControl() {
             return mParent;
         }
 
@@ -119,99 +222,9 @@
         public int getSurfaceHeight() {
             return 1;
         }
-    };
 
-    private final Runnable mFinishedCallback = () -> {
-        mFinishedCallbackCalled = true;
-    };
-
-    @Before
-    public void setUp() throws Exception {
-        super.setUp();
-        MockitoAnnotations.initMocks(this);
-        mParent = sWm.makeSurfaceBuilder(mSession)
-                .setName("test surface parent")
-                .setSize(3000, 3000)
-                .build();
-        mSurface = sWm.makeSurfaceBuilder(mSession)
-                .setName("test surface")
-                .setSize(1, 1)
-                .build();
-        mFinishedCallbackCalled = false;
-        mLeash = null;
-        mSurfaceAnimator = new SurfaceAnimator(mAnimatable, mFinishedCallback, sWm);
-    }
-
-    @Test
-    public void testRunAnimation() throws Exception {
-        mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
-        final ArgumentCaptor<OnAnimationFinishedCallback> callbackCaptor = ArgumentCaptor.forClass(
-                OnAnimationFinishedCallback.class);
-
-        assertTrue(mSurfaceAnimator.isAnimating());
-        assertNotNull(mSurfaceAnimator.getAnimation());
-        verify(mTransaction).reparent(eq(mSurface), eq(mLeash.getHandle()));
-        verify(mSpec).startAnimation(any(), any(), callbackCaptor.capture());
-
-        callbackCaptor.getValue().onAnimationFinished(mSpec);
-        assertFalse(mSurfaceAnimator.isAnimating());
-        assertNull(mSurfaceAnimator.getAnimation());
-        assertTrue(mFinishedCallbackCalled);
-
-        // TODO: Verify reparenting once we use mPendingTransaction to reparent it back
-    }
-
-    @Test
-    public void testOverrideAnimation() throws Exception {
-        mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
-        mSurfaceAnimator.startAnimation(mTransaction, mSpec2, true /* hidden */);
-
-        assertFalse(mFinishedCallbackCalled);
-
-        final ArgumentCaptor<OnAnimationFinishedCallback> callbackCaptor = ArgumentCaptor.forClass(
-                OnAnimationFinishedCallback.class);
-        assertTrue(mSurfaceAnimator.isAnimating());
-        assertNotNull(mSurfaceAnimator.getAnimation());
-        verify(mSpec).startAnimation(any(), any(), callbackCaptor.capture());
-
-        // First animation was finished, but this shouldn't cancel the second animation
-        callbackCaptor.getValue().onAnimationFinished(mSpec);
-        assertTrue(mSurfaceAnimator.isAnimating());
-
-        // Second animation was finished
-        verify(mSpec2).startAnimation(any(), any(), callbackCaptor.capture());
-        callbackCaptor.getValue().onAnimationFinished(mSpec2);
-        assertFalse(mSurfaceAnimator.isAnimating());
-        assertTrue(mFinishedCallbackCalled);
-    }
-
-    @Test
-    public void testCancelAnimation() throws Exception {
-        mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
-        assertTrue(mSurfaceAnimator.isAnimating());
-        mSurfaceAnimator.cancelAnimation();
-        assertFalse(mSurfaceAnimator.isAnimating());
-        verify(mSpec).onAnimationCancelled(any());
-        assertTrue(mFinishedCallbackCalled);
-    }
-
-    @Test
-    public void testDelayingAnimationStart() throws Exception {
-        mSurfaceAnimator.startDelayingAnimationStart();
-        mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
-        verifyZeroInteractions(mSpec);
-        assertTrue(mSurfaceAnimator.isAnimating());
-        mSurfaceAnimator.endDelayingAnimationStart();
-        verify(mSpec).startAnimation(any(), any(), any());
-    }
-
-    @Test
-    public void testDelayingAnimationStartAndCancelled() throws Exception {
-        mSurfaceAnimator.startDelayingAnimationStart();
-        mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
-        mSurfaceAnimator.cancelAnimation();
-        verifyZeroInteractions(mSpec);
-        assertFalse(mSurfaceAnimator.isAnimating());
-        assertTrue(mFinishedCallbackCalled);
+        private final Runnable mFinishedCallback = () -> {
+            mFinishedCallbackCalled = true;
+        };
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java
index 5cb9467..307deb4 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java
@@ -29,8 +29,6 @@
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
 
@@ -333,12 +331,19 @@
         final TestWindowContainer child12 = child1.addChildWindow(builder.setIsAnimating(true));
         final TestWindowContainer child21 = child2.addChildWindow();
 
-        assertTrue(root.isAnimating());
+        assertFalse(root.isAnimating());
         assertTrue(child1.isAnimating());
-        assertFalse(child11.isAnimating());
+        assertTrue(child11.isAnimating());
         assertTrue(child12.isAnimating());
         assertFalse(child2.isAnimating());
         assertFalse(child21.isAnimating());
+
+        assertTrue(root.isSelfOrChildAnimating());
+        assertTrue(child1.isSelfOrChildAnimating());
+        assertFalse(child11.isSelfOrChildAnimating());
+        assertTrue(child12.isSelfOrChildAnimating());
+        assertFalse(child2.isSelfOrChildAnimating());
+        assertFalse(child21.isSelfOrChildAnimating());
     }
 
     @Test
@@ -630,8 +635,8 @@
         }
 
         @Override
-        boolean isAnimating() {
-            return mIsAnimating || super.isAnimating();
+        boolean isSelfAnimating() {
+            return mIsAnimating;
         }
 
         @Override
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java
index b2334e8..5f58744 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java
@@ -189,7 +189,6 @@
     public static class TestTask extends Task {
         boolean mShouldDeferRemoval = false;
         boolean mOnDisplayChangedCalled = false;
-        private boolean mUseLocalIsAnimating = false;
         private boolean mIsAnimating = false;
 
         TestTask(int taskId, TaskStack stack, int userId, WindowManagerService service,
@@ -214,12 +213,11 @@
         }
 
         @Override
-        boolean isAnimating() {
-            return mUseLocalIsAnimating ? mIsAnimating : super.isAnimating();
+        boolean isSelfAnimating() {
+            return mIsAnimating;
         }
 
         void setLocalIsAnimating(boolean isAnimating) {
-            mUseLocalIsAnimating = true;
             mIsAnimating = isAnimating;
         }
     }
diff --git a/wifi/java/android/net/wifi/aware/PublishConfig.java b/wifi/java/android/net/wifi/aware/PublishConfig.java
index e60f52f..7a5049d 100644
--- a/wifi/java/android/net/wifi/aware/PublishConfig.java
+++ b/wifi/java/android/net/wifi/aware/PublishConfig.java
@@ -182,7 +182,7 @@
      *
      * @hide
      */
-    public void assertValid(Characteristics characteristics)
+    public void assertValid(Characteristics characteristics, boolean rttSupported)
             throws IllegalArgumentException {
         WifiAwareUtils.validateServiceName(mServiceName);
 
@@ -216,6 +216,10 @@
                         "Match filter longer than supported by device characteristics");
             }
         }
+
+        if (!rttSupported && mEnableRanging) {
+            throw new IllegalArgumentException("Ranging is not supported");
+        }
     }
 
     /**
@@ -364,6 +368,9 @@
          * Optional. Disabled by default - i.e. any peer which attempts to measure distance to this
          * device will be refused. If the peer has ranging enabled (using the
          * {@link SubscribeConfig} APIs listed above, it will never discover this device.
+         * <p>
+         * The device must support Wi-Fi RTT for this feature to be used. Feature support is checked
+         * as described in {@link android.net.wifi.rtt}.
          *
          * @param enable If true, ranging is supported on request of the peer.
          *
diff --git a/wifi/java/android/net/wifi/aware/SubscribeConfig.java b/wifi/java/android/net/wifi/aware/SubscribeConfig.java
index f6552a7..91f8e520 100644
--- a/wifi/java/android/net/wifi/aware/SubscribeConfig.java
+++ b/wifi/java/android/net/wifi/aware/SubscribeConfig.java
@@ -224,7 +224,7 @@
      *
      * @hide
      */
-    public void assertValid(Characteristics characteristics)
+    public void assertValid(Characteristics characteristics, boolean rttSupported)
             throws IllegalArgumentException {
         WifiAwareUtils.validateServiceName(mServiceName);
 
@@ -269,6 +269,10 @@
             throw new IllegalArgumentException(
                     "Maximum distance must be greater than minimum distance");
         }
+
+        if (!rttSupported && (mMinDistanceMmSet || mMaxDistanceMmSet)) {
+            throw new IllegalArgumentException("Ranging is not supported");
+        }
     }
 
     /**
@@ -422,6 +426,9 @@
          * peer must enable ranging using
          * {@link PublishConfig.Builder#setRangingEnabled(boolean)}. Otherwise discovery will
          * never be triggered.
+         * <p>
+         * The device must support Wi-Fi RTT for this feature to be used. Feature support is checked
+         * as described in {@link android.net.wifi.rtt}.
          *
          * @param minDistanceMm Minimum distance, in mm, to the publisher above which to trigger
          *                      discovery.
@@ -450,6 +457,9 @@
          * peer must enable ranging using
          * {@link PublishConfig.Builder#setRangingEnabled(boolean)}. Otherwise discovery will
          * never be triggered.
+         * <p>
+         * The device must support Wi-Fi RTT for this feature to be used. Feature support is checked
+         * as described in {@link android.net.wifi.rtt}.
          *
          * @param maxDistanceMm Maximum distance, in mm, to the publisher below which to trigger
          *                      discovery.